<template>
  <div class="page-wrapper">
    <!-- 引入字体组件 -->
    <font-loader />
    
    <!-- 顶部导航栏 -->
    <nav-bar :is-loggedin="false" :theme-icon="'fas fa-moon'" @toggle-theme="toggleTheme" @login="handleLogin" />

    <div class="home-root">
    <!-- 侧边栏组件 -->
    <chat-sidebar
      :visible.sync="showHistoryPanel"
      :conversations="conversations"
      :current-conversation-id="currentConversationId"
      app-title="会议纪要生成"
      default-conversation-name="新建工作"
      toggle-button-title="打开侧边栏"
      @new-chat="startNewChat"
      @load-conversation="loadConversation"
      @delete-chat="deleteChat"
    />

    <!-- 主内容区 -->
    <div class="main-content" :style="mainContentStyle">
      <!-- 聊天消息区域 -->
      <div class="chat-area" v-if="messages.length > 0">
        <div 
          class="messages-container" 
          ref="messagesContainer"
        >
          <div
            v-for="(msg, idx) in messages"
            :key="msg.messageId || `${msg.role}-${msg.created_at}-${idx}`"
            class="message-wrapper"
            :class="msg.role"
            v-show="msg.content || msg.role === 'user'"
          >
            <div class="message-item">
              <div class="message-avatar" v-if="msg.role === 'assistant'">
                <i class="el-icon-chat-dot-round"></i>
              </div>
              <div class="message-content">
                <!-- AI 回答内容 -->
                <div
                  v-if="msg.role === 'assistant'"
                  class="message-text"
                  v-html="formatMessage(msg.content)"
                ></div>
                <div class="message-actions" v-if="msg.role === 'assistant'">
                  <div class="message-time">{{ formatTime(msg.created_at) }}</div>
                  <div class="action-buttons">
                    <button
                      class="action-btn"
                      @click="copyMessage(msg.content)"
                      title="复制">
                      <i class="el-icon-document-copy"></i>
                    </button>
                    <button
                      class="action-btn"
                      @click="rethinkMessage(idx)"
                      :disabled="isLoading"
                      title="重新思考">
                      <i class="el-icon-refresh-left"></i>
                    </button>
                  </div>
                </div>

                <!-- 用户消息的内容和文件 -->
                <div v-if="msg.role === 'user'" class="user-message-content">
                  <div class="message-text user-message-text">
                    <div v-if="msg.uploadedFiles && msg.uploadedFiles.length > 0" class="user-files-inline">
                      <div
                        v-for="(file, fileIdx) in msg.uploadedFiles"
                        :key="fileIdx"
                        class="user-file-inline-item"
                        @click="previewFile(file)"
                      >
                        <i class="el-icon-document"></i>
                        <span class="user-file-inline-name">{{ file.name }}</span>
                      </div>
                    </div>
                    <div class="user-message-text-body" v-html="formatMessage(msg.content)"></div>
                  </div>
                </div>
                <div class="message-actions" v-if="msg.role === 'user'">
                  <div class="message-time">{{ formatTime(msg.created_at) }}</div>
                  <div class="action-buttons">
                    <button
                      class="action-btn"
                      @click="copyMessage(msg.content)"
                      title="复制">
                      <i class="el-icon-document-copy"></i>
                    </button>
                    <button
                      class="action-btn"
                      @click="resendMessage(msg.content)"
                      :disabled="isLoading"
                      title="重新发送">
                      <i class="el-icon-refresh"></i>
                    </button>
                  </div>
                </div>
              </div>
              <div class="message-avatar" v-if="msg.role === 'user'">
                <i class="el-icon-user-solid"></i>
              </div>
            </div>
          </div>

          <!-- 加载中提示 -->
          <div v-if="isLoading" class="message-wrapper assistant">
            <div class="message-item">
              <div class="message-avatar">
                <i class="el-icon-chat-dot-round"></i>
              </div>
              <div class="message-content">
                <div class="typing-indicator">
                  <div class="typing-text">正在思考中</div>
                  <div class="typing-dots">
                    <span></span>
                    <span></span>
                    <span></span>
                  </div>
                </div>
              </div>
            </div>
          </div>

          <!-- 下一步问题建议 -->
          <div v-if="!isLoading && currentSuggestedQuestions.length > 0" class="suggested-questions-container">
            <div class="suggested-questions-title">
              <i class="el-icon-question"></i>
              <span>你可能还想问</span>
            </div>
            <div class="suggested-questions-list">
              <div
                v-for="(question, idx) in currentSuggestedQuestions"
                :key="idx"
                class="suggested-question-chip"
                @click="setQuickMessage(question)">
                <span>{{ question }}</span>
                <i class="el-icon-arrow-right"></i>
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- 欢迎界面 -->
      <div class="welcome-area" v-else>
        <div class="welcome-content">
          <div class="welcome-icon">
            <i class="el-icon-edit-outline"></i>
          </div>
          <div class="title">{{ appConfig.openingStatement || '会议纪要生成助手' }}</div>
          <div class="subtitle" v-if="!appConfig.openingStatement && isAgentMode">智能记录会议内容，自动生成专业纪要，提升会议效率</div>

          <!-- 变量输入提示 -->
          <div class="file-variable-tip" v-if="shouldShowVariablesPanel && requiredVariables.length > 0">
            <i class="el-icon-warning"></i>
            <span>开始使用前，请先填写必填信息：{{ requiredVariables.map(v => v.label || v.variable).join('、') }}</span>
          </div>

          <!-- 推荐问题 -->
          <div class="suggested-questions" v-if="appConfig.suggestedQuestions.length > 0">
            <div
              v-for="(question, idx) in appConfig.suggestedQuestions"
              :key="idx"
              class="suggested-question-card"
              @click="setQuickMessage(question)">
              <i class="el-icon-tickets"></i>
              <span>{{ question }}</span>
            </div>
          </div>

          <!-- 默认快捷操作（仅在 Agent 模式下显示，如果没有推荐问题） -->
          <div class="quick-actions" v-else-if="isAgentMode">
            <div class="quick-action-card" @click="setQuickMessage('记录会议内容')">
              <i class="el-icon-edit"></i>
              <span>会议记录</span>
            </div>
            <div class="quick-action-card" @click="setQuickMessage('总结会议要点')">
              <i class="el-icon-data-analysis"></i>
              <span>内容总结</span>
            </div>
            <div class="quick-action-card" @click="setQuickMessage('生成会议纪要')">
              <i class="el-icon-document-add"></i>
              <span>纪要生成</span>
            </div>
          </div>
        </div>
      </div>

      <!-- 变量输入面板（隐藏独立面板，改为合并到输入框） -->
      <div class="variables-panel" v-if="false">
        <div class="panel-header" @click="variablesPanelCollapsed = !variablesPanelCollapsed">
          <div class="header-left">
            <i class="el-icon-setting"></i>
            <span class="panel-title">输入参数</span>
            <span class="variable-count">{{ appConfig.variables.length }} 个变量</span>
          </div>
          <i :class="variablesPanelCollapsed ? 'el-icon-arrow-down' : 'el-icon-arrow-up'"></i>
        </div>

        <div class="panel-content" v-show="!variablesPanelCollapsed">
          <div
            v-for="variable in appConfig.variables"
            :key="variable.variable || variable.name"
            class="variable-item">
            <div class="variable-label">
              <span>{{ variable.label || variable.variable }}</span>
              <span v-if="variable.required" class="required-mark">必填</span>
              <span v-else class="optional-mark">可选</span>
            </div>

            <!-- 文本输入 -->
            <el-input
              v-if="variable.type === 'text-input' || variable.type === 'text'"
              v-model="variableValues[variable.variable || variable.name]"
              :placeholder="'请输入' + (variable.label || variable.variable)"
              :maxlength="variable.max_length || 48"
              clearable
            />

            <!-- 段落输入 -->
            <el-input
              v-else-if="variable.type === 'paragraph'"
              type="textarea"
              v-model="variableValues[variable.variable || variable.name]"
              :placeholder="'请输入' + (variable.label || variable.variable)"
              :rows="3"
              :maxlength="variable.max_length || 1000"
              clearable
            />

            <!-- 数字输入 -->
            <el-input-number
              v-else-if="variable.type === 'number'"
              v-model="variableValues[variable.variable || variable.name]"
              :min="variable.min || 0"
              :max="variable.max || 999999"
              controls-position="right"
              style="width: 100%"
            />

            <!-- 选择器 -->
            <el-select
              v-else-if="variable.type === 'select'"
              v-model="variableValues[variable.variable || variable.name]"
              :placeholder="'请选择' + (variable.label || variable.variable)"
              style="width: 100%"
              clearable
            >
              <el-option
                v-for="option in variable.options"
                :key="option"
                :label="option"
                :value="option"
              />
            </el-select>

            <!-- 单文件上传 -->
            <div v-else-if="variable.type === 'file'" class="file-upload-area">
              <input
                :ref="'file_' + (variable.variable || variable.name)"
                type="file"
                class="hidden-file"
                @change="handleVariableFileSelect(variable, $event)"
              />

              <div v-if="!variableValues[variable.variable || variable.name]" class="upload-placeholder">
                <el-button
                  icon="el-icon-upload"
                  size="small"
                  @click="$refs['file_' + (variable.variable || variable.name)][0].click()">
                  选择文件
                </el-button>
                <span class="upload-hint">支持所有格式（不支持 .doc）</span>
              </div>

              <div v-else class="file-card">
                <i class="el-icon-document"></i>
                <div class="file-info">
                  <div class="file-name">{{ variableValues[variable.variable || variable.name].name }}</div>
                  <div class="file-size">{{ formatSize(variableValues[variable.variable || variable.name].size) }}</div>
                </div>
                <el-button
                  type="danger"
                  icon="el-icon-delete"
                  size="mini"
                  circle
                  @click="removeVariableFile(variable)">
                </el-button>
              </div>
            </div>

            <!-- 文件列表上传 -->
            <div v-else-if="variable.type === 'file-list'" class="file-list-upload-area">
              <input
                :ref="'filelist_' + (variable.variable || variable.name)"
                type="file"
                multiple
                class="hidden-file"
                @change="handleVariableFileListSelect(variable, $event)"
              />

              <el-button
                icon="el-icon-upload"
                size="small"
                @click="$refs['filelist_' + (variable.variable || variable.name)][0].click()">
                添加文件
              </el-button>
              <span class="upload-hint">支持所有格式（不支持 .doc）</span>

              <div v-if="variableValues[variable.variable || variable.name] && variableValues[variable.variable || variable.name].length > 0" class="file-list">
                <div
                  v-for="(file, idx) in variableValues[variable.variable || variable.name]"
                  :key="file.uid"
                  class="file-card">
                  <i class="el-icon-document"></i>
                  <div class="file-info">
                    <div class="file-name">{{ file.name }}</div>
                    <div class="file-size">{{ formatSize(file.size) }}</div>
                  </div>
                  <el-button
                    type="danger"
                    icon="el-icon-delete"
                    size="mini"
                    circle
                    @click="removeVariableFileFromList(variable, idx)">
                  </el-button>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- 输入区域 -->
      <div class="input-area" :style="inputAreaStyle">
        <div class="input-wrapper">
        <div class="composer" @keyup.enter="handleSend">
        <!-- 附件列表 - 有附件时显示 -->
        <div class="attachments" v-if="attachments.length">
              <div class="tip">已选择 {{ attachments.length }} 个文件</div>
          <div class="attachment-list">
            <div
              v-for="(file, idx) in attachments"
              :key="file.uid || idx"
              class="attachment-card"
            >
              <button class="att-remove" type="button" @click.stop="removeAttachment(idx)" title="移除">
                <i class="el-icon-close"></i>
              </button>
                  <div class="file-icon">
                <i class="el-icon-document"></i>
              </div>
              <div class="file-meta">
                <div class="file-name">{{ file.name }}</div>
                    <div class="file-size">{{ formatSize(file.size) }}</div>
              </div>
            </div>
          </div>
        </div>
        <div class="input-row">
              <textarea
            v-model="message"
                ref="textarea"
            class="input"
                rows="1"
                placeholder="输入会议内容或问题 (Enter 发送, Shift+Enter 换行)"
                @keydown="handleKeydown"
                :disabled="isLoading"
              ></textarea>
          <div class="actions">
                <!-- 变量输入参数按钮（弹出面板） -->
                <el-popover
                  v-if="shouldShowVariablesPanel"
                  ref="variablesPopover"
                  placement="top"
                  width="520"
                  trigger="manual"
                  v-model="variablesPopoverVisible"
                  popper-class="composer-variables-popover">
                  <div class="composer-variables">
                    <div class="variable-item" v-for="variable in appConfig.variables" :key="variable.variable || variable.name">
                      <div class="variable-label">
                        <span>{{ variable.label || variable.variable }}</span>
                        <span v-if="variable.required" class="required-mark">必填</span>
                        <span v-else class="optional-mark">可选</span>
                      </div>

                      <!-- 文本输入 -->
                      <el-input
                        v-if="variable.type === 'text-input' || variable.type === 'text'"
                        v-model="variableValues[variable.variable || variable.name]"
                        :placeholder="'请输入' + (variable.label || variable.variable)"
                        :maxlength="variable.max_length || 48"
                        clearable
                      />

                      <!-- 段落输入 -->
                      <el-input
                        v-else-if="variable.type === 'paragraph'"
                        type="textarea"
                        v-model="variableValues[variable.variable || variable.name]"
                        :placeholder="'请输入' + (variable.label || variable.variable)"
                        :rows="3"
                        :maxlength="variable.max_length || 1000"
                        clearable
                      />

                      <!-- 数字输入 -->
                      <el-input-number
                        v-else-if="variable.type === 'number'"
                        v-model="variableValues[variable.variable || variable.name]"
                        :min="variable.min || 0"
                        :max="variable.max || 999999"
                        controls-position="right"
                        style="width: 100%"
                      />

                      <!-- 选择器 -->
                      <el-select
                        v-else-if="variable.type === 'select'"
                        v-model="variableValues[variable.variable || variable.name]"
                        :placeholder="'请选择' + (variable.label || variable.variable)"
                        style="width: 100%"
                        clearable
                      >
                        <el-option
                          v-for="option in variable.options"
                          :key="option"
                          :label="option"
                          :value="option"
                        />
                      </el-select>

                      <!-- 单文件上传 -->
                      <div v-else-if="variable.type === 'file'" class="file-upload-area">
                        <input
                          :ref="'file_' + (variable.variable || variable.name)"
                          type="file"
                          class="hidden-file"
                          @change="handleVariableFileSelect(variable, $event)"
                        />

                        <div v-if="!variableValues[variable.variable || variable.name]" class="upload-placeholder">
                          <el-button
                            icon="el-icon-upload"
                            size="small"
                            @click="$refs['file_' + (variable.variable || variable.name)][0].click()">
                            选择文件
                          </el-button>
                          <span class="upload-hint">支持所有格式（不支持 .doc）</span>
                        </div>

                        <div v-else class="file-card">
                          <i class="el-icon-document"></i>
                          <div class="file-info">
                            <div class="file-name">{{ variableValues[variable.variable || variable.name].name }}</div>
                            <div class="file-size">{{ formatSize(variableValues[variable.variable || variable.name].size) }}</div>
                          </div>
                          <el-button
                            type="danger"
                            icon="el-icon-delete"
                            size="mini"
                            circle
                            @click="removeVariableFile(variable)">
                          </el-button>
                        </div>
                      </div>

                      <!-- 文件列表上传 -->
                      <div v-else-if="variable.type === 'file-list'" class="file-list-upload-area">
                        <input
                          :ref="'filelist_' + (variable.variable || variable.name)"
                          type="file"
                          multiple
                          class="hidden-file"
                          @change="handleVariableFileListSelect(variable, $event)"
                        />

                        <el-button
                          icon="el-icon-upload"
                          size="small"
                          @click="$refs['filelist_' + (variable.variable || variable.name)][0].click()">
                          添加文件
                        </el-button>
                        <span class="upload-hint">支持所有格式（不支持 .doc）</span>

                        <div v-if="variableValues[variable.variable || variable.name] && variableValues[variable.variable || variable.name].length > 0" class="file-list">
                          <div
                            v-for="(file, idx) in variableValues[variable.variable || variable.name]"
                            :key="file.uid"
                            class="file-card">
                            <i class="el-icon-document"></i>
                            <div class="file-info">
                              <div class="file-name">{{ file.name }}</div>
                              <div class="file-size">{{ formatSize(file.size) }}</div>
                            </div>
                            <el-button
                              type="danger"
                              icon="el-icon-delete"
                              size="mini"
                              circle
                              @click="removeVariableFileFromList(variable, idx)">
                            </el-button>
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>
                  <div slot="reference" class="popover-center-ref"></div>
                </el-popover>
                <button
                  v-if="shouldShowVariablesPanel"
                  class="attach parameters-btn"
                  :class="{ alert: hasMissingRequired }"
                  type="button"
                  :disabled="isLoading"
                  @click="variablesPopoverVisible = !variablesPopoverVisible">
                  <i class="el-icon-setting"></i>
                  <sup v-if="missingRequiredCount > 0" class="badge">{{ missingRequiredCount }}</sup>
                </button>

                <!-- Chatflow 文件上传按钮 -->
                <template v-if="shouldShowFileUpload">
                  <input ref="chatflowFile" class="hidden-file" type="file" multiple accept="*" @change="handleChatflowFileUpload" />
                  <button class="attach" type="button" @click="triggerChatflowFileUpload" title="上传文件（多模态对话）" :disabled="isLoading">
                    <i class="el-icon-paperclip"></i>
                  </button>
                </template>
                <button v-if="isLoading" class="stop" type="button" @click="stopGeneration" title="停止生成">
                  <i class="el-icon-video-pause"></i>
                </button>
                <button v-else class="send" type="button" @click="handleSend" title="发送" :disabled="!message.trim()">
                  <i class="el-icon-s-promotion"></i>
            </button>
          </div>
        </div>
        </div>
      </div>
        </div>
            </div>
            </div>
    </div>
</template>

<script>
import { mapState } from 'vuex'
import NavBar from '@/layout/UserView/NavBar.vue'
import FontLoader from '@/components/UserView/general/font.vue'
import ChatSidebar from '@/components/UserView/general/sidebar.vue'
import { sendChatMessage, uploadFile, getConversations, deleteConversation, stopChatMessage, getMessages, getAppParameters } from '@/api/dify/meetingMinutes'

// 全局函数：切换分析过程显示/隐藏
window.toggleThinkBlock = function(thinkId) {
  const content = document.getElementById(`content-${thinkId}`)
  const icon = document.getElementById(`icon-${thinkId}`)

  if (content && icon) {
    if (content.classList.contains('collapsed')) {
      // 展开
      content.classList.remove('collapsed')
      icon.classList.remove('el-icon-arrow-down')
      icon.classList.add('el-icon-arrow-up')
    } else {
      // 折叠
      content.classList.add('collapsed')
      icon.classList.remove('el-icon-arrow-up')
      icon.classList.add('el-icon-arrow-down')
    }
  }
}

export default {
  name: 'MeetingMinutes',
  components: {
    NavBar,
    FontLoader,
    ChatSidebar
  },
  data() {
    return {
      appName: 'meetingMinutes', // App名称，用于后端查询对应的API Key
      message: '',
      messages: [],
      attachments: [],
      sidebarCollapsed: false,
      showHistoryPanel: true,
      currentConversationId: null,
      conversations: [],
      isLoading: false,
      isLoadingHistory: false,  // 新增：历史记录加载状态
      currentTaskId: null,
      abortController: null,
      streamController: null,
      // Dify 应用配置
      appConfig: {
        openingStatement: '',
        suggestedQuestions: [],
        suggestedQuestionsAfterAnswer: false,
        fileUploadEnabled: false, // Chatflow 文件上传功能是否启用（对应 sys.files）
        userInputForm: [], // 用户输入表单配置（包含所有变量定义）
        variables: [], // 所有类型的变量列表（从 userInputForm 中提取）
        mode: '' // 应用模式：agent, chat, completion, workflow 等
      },
      // 当前消息的下一步建议
      currentSuggestedQuestions: [],
      // 变量输入面板
      variablesPanelCollapsed: false, // 变量面板是否折叠
      variableValues: {}, // 当前输入的变量值 { variableName: value }
      // 当前会话已提交的变量（用于续接会话）
      conversationInputs: {}, // 存储已提交的变量，用于续接会话时重复传递
      // 变量弹出面板控制
      variablesPopoverVisible: false
    }
  },
  computed: {
    // 是否显示文件上传按钮（仅判断 Chatflow 文件上传功能）
    shouldShowFileUpload() {
      return this.appConfig.fileUploadEnabled
    },

    // 是否显示变量输入面板
    shouldShowVariablesPanel() {
      return this.appConfig.variables.length > 0
    },

    // 获取必填的变量
    requiredVariables() {
      return this.appConfig.variables.filter(v => v.required)
    },

    // 是否为 Agent 模式
    isAgentMode() {
      return this.appConfig.mode === 'agent'
    },

    // 未填必填项数量与状态（用于显著提示）
    missingRequiredCount() {
      if (!this.appConfig || !this.appConfig.variables) return 0
      const required = this.appConfig.variables.filter(v => v.required)
      let count = 0
      required.forEach(v => {
        const key = v.variable || v.name
        const val = this.variableValues[key]
        if (v.type === 'file' || v.type === 'file-list') {
          if (!val || (Array.isArray(val) && val.length === 0)) count++
        } else if (!val || val.toString().trim() === '') {
          count++
        }
      })
      return count
    },

    hasMissingRequired() {
      return this.missingRequiredCount > 0 && !this.currentConversationId
    },

    // 主内容区动态居中样式（将消息区和输入框作为整体进行居中）
    mainContentStyle() {
      // 侧边栏宽度为 300px
      const sidebarWidth = 300
      // 当侧边栏打开时，向右偏移侧边栏宽度的一半，使整个内容区在剩余空间居中
      const offset = this.showHistoryPanel ? sidebarWidth / 2 : 0
      return {
        marginLeft: `${offset}px`,
        transition: 'margin-left 0.3s ease'
      }
    },

    // 输入区域动态定位样式（因为是 fixed 定位，需要单独处理）
    inputAreaStyle() {
      const sidebarWidth = 300
      const offset = this.showHistoryPanel ? sidebarWidth / 2 : 0
      return {
        left: `${offset}px`,
        transition: 'left 0.3s ease'
      }
    },
    
    // 从 Vuex 获取用户ID
    ...mapState({
      userId: state => state.user.userSn
    })
  },
  async mounted() {
    // 从路由参数获取 appName（如果提供）
    if (this.$route.query.appName) {
      this.appName = this.$route.query.appName
    }

    // 如果 userSn 还未加载，先获取用户信息
    if (!this.userId) {
      await this.$store.dispatch('user/getInfo')
    }

    await this.loadAppConfig()
    await this.loadConversationsList()
    this.autoResizeTextarea()

    // 如果有会话历史，可以选择自动加载最近的一个对话（可选）
    // if (this.conversations.length > 0) {
    //   await this.loadConversation(this.conversations[0].id)
    // }

    // 添加全局点击监听，用于点击外部关闭弹窗
    document.addEventListener('click', this.handleClickOutside)
  },
  beforeDestroy() {
    // 组件销毁时清理资源
    if (this.abortController) {
      this.abortController.abort()
    }
    if (this.streamController && this.streamController.abort) {
      this.streamController.abort()
    }

    // 移除全局点击监听
    document.removeEventListener('click', this.handleClickOutside)
  },
  methods: {
    /**
     * 处理点击外部关闭弹窗
     */
    handleClickOutside(event) {
      // 处理变量弹窗
      if (this.variablesPopoverVisible) {
        const popoverEl = this.$refs.variablesPopover?.$el
        const popperEl = document.querySelector('.composer-variables-popover')
        const targetBtn = event.target.closest('.parameters-btn')
        if (!targetBtn && !popoverEl?.contains(event.target) && !popperEl?.contains(event.target)) {
          this.variablesPopoverVisible = false
        }
      }

      // 历史抽屉只能通过关闭按钮关闭，不响应外部点击
    },

    async loadAppConfig() {
      try {
        console.log('🔄 正在加载应用配置... App名称:', this.appName)
        const response = await getAppParameters(this.appName)

        // 打印完整的 API 返回数据，方便调试
        console.log('📥 API 返回的完整配置:', JSON.stringify(response, null, 2))

        // 提取 data 属性
        const config = response?.data || response

        if (config) {
          this.appConfig.openingStatement = config.opening_statement || ''
          this.appConfig.suggestedQuestions = config.suggested_questions || []
          this.appConfig.suggestedQuestionsAfterAnswer = config.suggested_questions_after_answer?.enabled || false

          // 读取应用模式
          this.appConfig.mode = (config.mode || config.app_mode || '').toLowerCase()

          // 打印推荐问题配置
          console.log('🎯 推荐问题配置:', {
            '欢迎界面推荐问题': this.appConfig.suggestedQuestions.length > 0 ? this.appConfig.suggestedQuestions : '未配置',
            '消息后推荐问题': this.appConfig.suggestedQuestionsAfterAnswer ? '已启用' : '未启用',
            '原始配置': config.suggested_questions_after_answer
          })

          // 1. 检查 Chatflow 文件上传功能是否启用（对应 sys.files）
          // 这个功能用于多模态对话，文件通过 files 参数传递
          this.appConfig.fileUploadEnabled =
            config.file_upload?.enabled ||
            config.features?.file_upload?.enabled ||
            false

          console.log('🔍 检查文件上传配置:', {
            'config.file_upload': config.file_upload,
            'config.features': config.features,
            'fileUploadEnabled': this.appConfig.fileUploadEnabled
          })

          // 2. 获取用户输入表单配置（可能包含文件变量定义）
          // 文件变量用于工作流输入，文件通过 inputs 参数传递
          this.appConfig.userInputForm = config.user_input_form || []

          console.log('🔍 用户输入表单配置:', {
            'config.user_input_form': config.user_input_form,
            'userInputFormLength': this.appConfig.userInputForm.length,
            'userInputFormDetail': this.appConfig.userInputForm
          })

          // 3. 提取所有类型的变量列表
          // 注意：user_input_form 的结构是 [{变量名: {配置}}]
          this.appConfig.variables = []
          this.appConfig.userInputForm.forEach(inputWrapper => {
            // inputWrapper 是类似 {"variable_name": {...}} 的对象
            Object.keys(inputWrapper).forEach(varName => {
              const inputConfig = inputWrapper[varName]
              // 添加所有类型的变量
              // 确保 variable 字段存在且与 key 一致
              this.appConfig.variables.push({
                ...inputConfig,
                variable: inputConfig.variable || varName, // 使用配置中的 variable 字段或使用 key
                name: varName // 保留原始 key
              })
            })
          })

          // 初始化变量默认值
          const initialValues = {}
          this.appConfig.variables.forEach(v => {
            const varName = v.variable || v.name
            // 根据类型设置默认值
            if (v.type === 'file' || v.type === 'file-list') {
              initialValues[varName] = v.type === 'file-list' ? [] : null
            } else if (v.type === 'select') {
              initialValues[varName] = v.default || (v.options && v.options[0]) || ''
            } else {
              initialValues[varName] = v.default || ''
            }
          })
          // 一次性设置所有属性，确保响应式
          this.variableValues = initialValues

          console.log('📋 变量默认值初始化:', initialValues)
          console.log('📋 变量详细信息:', this.appConfig.variables.map(v => ({
            variable: v.variable,
            name: v.name,
            label: v.label,
            type: v.type,
            required: v.required,
            defaultValue: initialValues[v.variable || v.name]
          })))

          console.log('📋 应用配置加载完成:', {
            mode: this.appConfig.mode,
            isAgentMode: this.isAgentMode,
            fileUploadEnabled: this.appConfig.fileUploadEnabled,
            variablesCount: this.appConfig.variables.length,
            variables: this.appConfig.variables.map(v => ({
              variable: v.variable,
              name: v.name,
              label: v.label,
              type: v.type,
              required: v.required
            })),
            shouldShowVariablesPanel: this.shouldShowVariablesPanel,
            shouldShowFileUpload: this.shouldShowFileUpload
          })

          // 显示配置说明
          console.log('ℹ️ 应用模式:', this.appConfig.mode || '未指定')
          if (this.appConfig.variables.length > 0) {
            const varSummary = this.appConfig.variables.map(v =>
              `${v.label || v.variable}[${v.type}]${v.required ? '(必填)' : '(可选)'}`
            ).join('、')
            console.log('ℹ️ 变量配置:', varSummary)
          }
          if (this.appConfig.fileUploadEnabled) {
            console.log('ℹ️ 文件上传配置: 启用 Chatflow 文件上传（sys.files）')
          }
          if (this.isAgentMode) {
            console.log('ℹ️ Agent 模式: 将显示快捷操作卡片（如无推荐问题）')
          }
          if (this.appConfig.suggestedQuestions.length > 0) {
            console.log('ℹ️ 欢迎界面推荐问题:', this.appConfig.suggestedQuestions.length, '个')
          }
          if (this.appConfig.suggestedQuestionsAfterAnswer) {
            console.log('ℹ️ 消息后推荐问题: 已启用，AI 回复后可能显示推荐问题')
          }
        } else {
          console.warn('⚠️ API 未返回配置数据')
        }
      } catch (error) {
        console.error('❌ 加载应用配置失败:', error)
        console.error('错误详情:', {
          message: error.message,
          response: error.response?.data,
          status: error.response?.status
        })
      }
    },

    async handleSend() {
      if (!this.message.trim() || this.isLoading) return

      // 新会话时，校验必填变量
      if (!this.currentConversationId && this.appConfig.variables.length > 0) {
        const missingRequired = this.requiredVariables.filter(v => {
          const varName = v.variable || v.name
          const value = this.variableValues[varName]
          // 检查值是否为空
          if (v.type === 'file' || v.type === 'file-list') {
            return !value || (Array.isArray(value) && value.length === 0)
          }
          return !value || value.toString().trim() === ''
        })

        if (missingRequired.length > 0) {
          const varNames = missingRequired.map(v => v.label || v.variable).join('、')
          this.$message.error(`请先填写必填变量：${varNames}`)
          // 打开变量弹出面板
          this.variablesPopoverVisible = true
          return
        }
      }

      const userMessage = this.message.trim()
      this.message = ''

      const currentTime = Date.now()

      // 清空当前的建议问题（发送新消息时）
      this.currentSuggestedQuestions = []

      // 保存上传的文件信息（用于显示和预览）
      const uploadedFilesInfo = this.attachments.length > 0
        ? this.attachments.map(f => ({
            name: f.name,
            size: f.size,
            raw: f.raw,
            url: f.raw ? URL.createObjectURL(f.raw) : null
          }))
        : []

      // 添加用户消息
      this.messages.push({
        role: 'user',
        content: userMessage,
        created_at: currentTime,
        messageId: `temp-user-${currentTime}`,
        uploadedFiles: uploadedFilesInfo // 保存文件信息用于显示
      })

      // 滚动到底部
      this.$nextTick(() => {
        this.scrollToBottom()
      })

      this.isLoading = true

      // 准备文件参数和输入参数
      const files = []  // 用于 Chatflow 文件上传（sys.files）
      const inputs = {} // 用于工作流变量

      // 处理 Chatflow 文件上传
      if (this.attachments.length > 0) {
        try {
          for (const file of this.attachments) {
            const uploadResult = await uploadFile(file.raw, this.userId, this.appName)
            // 兼容不同的响应格式
            const fileId = uploadResult.data?.id || uploadResult.id
            files.push({
              type: this.getFileType(file.name),
              transfer_method: 'local_file',
              upload_file_id: fileId
            })
          }
          console.log('📤 已上传 ' + this.attachments.length + ' 个文件到 sys.files')
        } catch (error) {
          this.$message.error('文件上传失败：' + error.message)
          this.isLoading = false
          return
        }
      }

      // 处理变量输入（包括文件变量）
      if (this.appConfig.variables.length > 0) {
        try {
          for (const variable of this.appConfig.variables) {
            const varName = variable.variable || variable.name
            const value = this.variableValues[varName]

            // 处理文件类型变量
            if (variable.type === 'file' || variable.type === 'file-list') {
              if (value) {
                if (Array.isArray(value)) {
                  // file-list
                  const uploadedFiles = []
                  for (const file of value) {
                    const uploadResult = await uploadFile(file.raw, this.userId, this.appName)
                    // 兼容不同的响应格式
                    const fileId = uploadResult.data?.id || uploadResult.id
                    uploadedFiles.push({
                      type: this.getFileType(file.name),
                      transfer_method: 'local_file',
                      upload_file_id: fileId,
                      name: file.name
                    })
                  }
                  inputs[varName] = uploadedFiles
                } else {
                  // single file
                  const uploadResult = await uploadFile(value.raw, this.userId, this.appName)
                  // 兼容不同的响应格式
                  const fileId = uploadResult.data?.id || uploadResult.id
                  inputs[varName] = {
                    type: this.getFileType(value.name),
                    transfer_method: 'local_file',
                    upload_file_id: fileId,
                    name: value.name
                  }
                }
              }
            } else {
              // 处理其他类型变量
              if (value !== null && value !== undefined && value !== '') {
                inputs[varName] = value
              }
            }
          }

          console.log('📤 变量输入:', Object.keys(inputs).join(', '))
        } catch (error) {
          this.$message.error('变量处理失败：' + error.message)
          this.isLoading = false
          return
        }
      }

      // 准备 AI 消息占位
      let aiMessageIndex = this.messages.length
      this.messages.push({
        role: 'assistant',
        content: '',
        created_at: currentTime + 1,
        messageId: `temp-assistant-${currentTime}`
      })

      // 创建 AbortController
      this.abortController = new AbortController()

      // 临时存储当前消息的思考内容
      let currentThoughts = []
      let currentFiles = []

      // 准备请求参数
      const requestParams = {
        query: userMessage,
        user: this.userId,
        conversation_id: this.currentConversationId,
        signal: this.abortController.signal,
        appName: this.appName
      }

      // 处理 files（Chatflow 文件上传）
      if (files.length > 0) {
        requestParams.files = files
      }

      // 处理 inputs（工作流变量）
      if (!this.currentConversationId) {
        // 新会话 - 传递所有变量
        if (Object.keys(inputs).length > 0) {
          requestParams.inputs = inputs
          this.conversationInputs = JSON.parse(JSON.stringify(inputs))
          console.log('📤 新会话 - 传递变量到 inputs，并保存供后续使用')
        } else if (this.appConfig.variables.length > 0) {
          requestParams.inputs = {}
          console.log('📤 新会话 - 传递空 inputs')
        }
      } else {
        // 续接会话 - 继续使用之前的变量
        if (this.appConfig.variables.length > 0) {
          if (Object.keys(inputs).length > 0) {
            // 用户修改了变量
            requestParams.inputs = inputs
            this.conversationInputs = JSON.parse(JSON.stringify(inputs))
            console.log('📤 续接会话 - 用户修改了变量，使用新值')
          } else if (Object.keys(this.conversationInputs).length > 0) {
            // 使用之前的变量
            requestParams.inputs = this.conversationInputs
            console.log('📤 续接会话 - 使用之前的变量:', this.conversationInputs)
          } else {
            requestParams.inputs = {}
            console.log('📤 续接会话 - 无变量可用，传递空 inputs')
          }
        }
      }

      // 打印发送的参数（调试用）
      console.log('📤 最终发送参数:', {
        query: userMessage.substring(0, 50) + '...',
        user: this.userId,
        conversation_id: this.currentConversationId,
        files: requestParams.files,
        inputs: requestParams.inputs,
        hasFiles: !!requestParams.files,
        hasInputs: !!requestParams.inputs,
        filesLength: requestParams.files?.length,
        inputsKeys: requestParams.inputs ? Object.keys(requestParams.inputs) : undefined
      })

      // 发送消息
      this.streamController = sendChatMessage(
        requestParams,
        // onMessage
        (data) => {
          // 保存 task_id
          if (data.task_id && !this.currentTaskId) {
            this.currentTaskId = data.task_id
          }

          if (!this.messages[aiMessageIndex]) return

          // 根据事件类型处理
          switch (data.event) {
            case 'agent_thought':
              // Agent 思考事件
              if (data.thought) {
                const existingThought = currentThoughts.find(t => t.id === data.id)
                if (!existingThought) {
                  currentThoughts.push({
                    id: data.id,
                    thought: data.thought,
                    tool: data.tool || '',
                    observation: data.observation || '',
                    position: data.position || currentThoughts.length + 1
                  })
                } else {
                  // 更新已有的思考
                  Object.assign(existingThought, {
                    thought: data.thought,
                    tool: data.tool || existingThought.tool,
                    observation: data.observation || existingThought.observation
                  })
                }
              }
              break

            case 'message_file':
              // 文件事件
              if (data.file) {
                const existingFile = currentFiles.find(f => f.id === data.file.id)
                if (!existingFile) {
                  currentFiles.push(data.file)
                }
              }
              break

            case 'message':
            case 'agent_message':
              // 常规消息事件 - answer 是增量的
              if (data.answer) {
                // 将新的内容追加到消息中（但要重新构建完整内容）
                this.messages[aiMessageIndex].partialAnswer =
                  (this.messages[aiMessageIndex].partialAnswer || '') + data.answer
              }
              break
          }

          // 重新构建完整内容
          let fullContent = ''

          const answerContent = this.messages[aiMessageIndex].partialAnswer || ''
          const answerHasThinkTags = answerContent.includes('<think>')

          // 【核心逻辑优化】
          // 1. 优先使用 Dify API 返回的结构化数据（agent_thought 事件）
          // 2. 如果没有 agent_thought 事件，尝试从 answer 中智能识别思考过程
          // 3. 最后才使用开发者自定义的 <think> 标签

          if (!answerHasThinkTags && currentThoughts.length > 0) {
            // 场景1：从 Dify API 的 agent_thought 事件构建思考过程
            currentThoughts.sort((a, b) => a.position - b.position)

            currentThoughts.forEach((thought, idx) => {
              if (thought.thought) {
                let thinkContent = `**分析过程 ${idx + 1}:**\n${thought.thought.trim()}`

                if (thought.tool) {
                  thinkContent += `\n\n**使用工具:** ${thought.tool}`
                }
                if (thought.observation) {
                  thinkContent += `\n\n**执行结果:** ${thought.observation}`
                }

                // 统一使用 <think> 标签包裹思考内容
                fullContent += `<think>${thinkContent}</think>`
              }
            })
            console.log(`✅ 从 agent_thought 事件构建了 ${currentThoughts.length} 个思考块`)
          } else if (!answerHasThinkTags && answerContent && this.detectThinkingContent(answerContent)) {
            // 场景2：智能检测 answer 中的思考过程
            const { thinkingPart, answerPart } = this.extractThinkingContent(answerContent)
            if (thinkingPart) {
              fullContent += `<think>${thinkingPart}</think>`
              console.log('✅ 智能识别到 answer 中的思考过程')
              // 只添加非思考部分到回答中
              if (answerPart) {
                fullContent += answerPart
              }
            } else {
              fullContent += answerContent
            }
          } else if (answerHasThinkTags) {
            // 场景3：使用开发者自定义的 <think> 标签
            console.log('ℹ️ answer 中已包含 <think> 标签，使用开发者自定义的思考内容')
            fullContent += answerContent
          } else {
            // 场景4：普通回答，直接添加
            fullContent += answerContent
          }

          // 3. 添加文件
          if (currentFiles.length > 0) {
            fullContent += '\n\n'
            currentFiles.forEach(file => {
              if (file.type === 'image') {
                fullContent += `\n<img src="${file.url}" alt="生成的图片" style="max-width: 100%; border-radius: 8px; margin-top: 8px;" />`
              } else {
                // 尝试多种方式获取文件名
                let fileName = file.name || file.filename || file.title

                // 尝试从 belongs_to 字段获取
                if (!fileName && file.belongs_to) {
                  fileName = file.belongs_to.name || file.belongs_to.filename
                }

                // 尝试从 extension_data 获取
                if (!fileName && file.extension_data) {
                  fileName = file.extension_data.name || file.extension_data.filename
                }

                // 尝试从 URL 查询参数中获取
                if (!fileName && file.url) {
                  try {
                    const urlObj = new URL(file.url)
                    // 检查查询参数
                    const nameParam = urlObj.searchParams.get('name') ||
                                     urlObj.searchParams.get('filename') ||
                                     urlObj.searchParams.get('file_name')
                    if (nameParam) {
                      fileName = decodeURIComponent(nameParam)
                    } else {
                      // 尝试从路径提取
                      const pathParts = urlObj.pathname.split('/')
                      const lastPart = pathParts[pathParts.length - 1]
                      if (lastPart && lastPart !== 'file-preview') {
                        fileName = decodeURIComponent(lastPart)
                      }
                    }
                  } catch (e) {
                    console.warn('无法从 URL 提取文件名:', e)
                  }
                }

                // 最后的回退选项
                if (!fileName || fileName === 'file-preview') {
                  fileName = `${file.type || 'document'}文件`
                }

                fullContent += `\n<div class="file-link-wrapper"><i class="el-icon-document"></i> <a href="${file.url}" target="_blank" class="file-link">${fileName}</a></div>`
              }
            })
          }

          // 更新消息内容
          this.messages[aiMessageIndex].content = fullContent
          
          // 【关键】当开始接收到内容时，立即关闭 loading 状态，避免显示两个头像
          if (fullContent && this.isLoading) {
            this.isLoading = false
          }
          
          this.$nextTick(() => {
            this.scrollToBottom()
          })
        },
        // onEnd
        (data) => {
          this.isLoading = false
          this.currentTaskId = null
          this.abortController = null
          this.streamController = null
          this.attachments = []

          // 重置临时变量
          currentThoughts = []
          currentFiles = []

          // 保存 conversation_id
          if (data.conversation_id && !this.currentConversationId) {
            this.currentConversationId = data.conversation_id
            this.loadConversationsList()
          }

          // 处理引用和归属
          if (data.metadata?.retriever_resources && data.metadata.retriever_resources.length > 0) {
            const retrieverResources = data.metadata.retriever_resources
            let referencesHtml = '\n\n<div class="references-section">'
            referencesHtml += '<div class="references-title"><i class="el-icon-link"></i> 引用和归属</div>'
            retrieverResources.forEach((resource, idx) => {
              referencesHtml += `<div class="reference-item">`
              referencesHtml += `<div class="reference-header">`
              referencesHtml += `<span class="reference-index">${idx + 1}</span>`
              referencesHtml += `<span class="reference-dataset">${resource.dataset_name}</span>`
              referencesHtml += `<span class="reference-score">相似度: ${(resource.score * 100).toFixed(1)}%</span>`
              referencesHtml += `</div>`
              referencesHtml += `<div class="reference-content">${resource.content?.substring(0, 200) || ''}...</div>`
              referencesHtml += `</div>`
            })
            referencesHtml += '</div>'

            // 添加到最后一条 AI 消息
            if (this.messages[aiMessageIndex]) {
              this.messages[aiMessageIndex].content += referencesHtml
              this.messages[aiMessageIndex].hasReferences = true
            }
          }

          // 处理下一步问题建议
          if (this.appConfig.suggestedQuestionsAfterAnswer && data.metadata?.suggested_questions) {
            this.currentSuggestedQuestions = data.metadata.suggested_questions
            console.log('✨ 收到推荐问题:', this.currentSuggestedQuestions)
          } else {
            this.currentSuggestedQuestions = []
            if (this.appConfig.suggestedQuestionsAfterAnswer && !data.metadata?.suggested_questions) {
              console.log('⚠️ 已启用消息后推荐问题功能，但本次响应未返回推荐问题')
            }
          }

          this.$nextTick(() => {
            this.scrollToBottom()
            // 自动折叠思考过程
            this.autoCollapseThinkBlocks()
          })
        },
        // onError
        (error) => {
          this.isLoading = false
          this.currentTaskId = null
          this.abortController = null
          this.streamController = null

          // 重置临时变量
          currentThoughts = []
          currentFiles = []

          // 如果是 AbortError，说明是用户主动停止，不显示错误消息
          if (error.name !== 'AbortError') {
            this.$message.error('发送失败：' + (error.message || '未知错误'))
          } else {
            console.log('✅ 用户已停止生成')
          }
        }
      )
    },

    handleKeydown(e) {
      if (e.key === 'Enter' && !e.shiftKey) {
        e.preventDefault()
        this.handleSend()
      }
    },

    async stopGeneration() {
      if (!this.isLoading) {
        this.$message.warning('没有正在进行的任务')
        return
      }

      try {
        // 1. 中断 fetch 请求
        if (this.abortController) {
          try {
            this.abortController.abort()
          } catch (e) {
            // AbortError 是正常的，忽略
          }
        }

        // 2. 取消 stream reader
        if (this.streamController && this.streamController.abort) {
          try {
            this.streamController.abort()
          } catch (e) {
            // AbortError 是正常的，忽略
          }
        }

        // 3. 调用 Dify 停止 API（如果有 task_id）
        if (this.currentTaskId) {
          try {
            await stopChatMessage(this.currentTaskId, this.userId)
          } catch (apiError) {
            // 忽略停止 API 的错误
          }
        }

        // 4. 清理状态
        this.isLoading = false
        this.currentTaskId = null
        this.abortController = null
        this.streamController = null

        this.$message.success('已停止生成')
      } catch (error) {
        // 捕获并忽略 AbortError
        if (error.name !== 'AbortError') {
          console.error('停止失败:', error)
        }

        // 无论如何都要清理状态
        this.isLoading = false
        this.currentTaskId = null
        this.abortController = null
        this.streamController = null

        this.$message.info('已终止')
      }
    },

    formatMessage(content) {
      if (!content) return ''

      // 先清理可能存在的不完整标签
      let cleanContent = content
        .replace(/<think>\s*$/i, '') // 移除末尾的不完整开始标签
        .replace(/^\s*<\/think>/i, '') // 移除开头的不完整结束标签

      let result = ''
      let lastIndex = 0
      let thinkBlockCount = 0

      // 使用正则匹配 <think>...</think> 标签
      const thinkRegex = /<think>([\s\S]*?)<\/think>/gi
      let match

      while ((match = thinkRegex.exec(cleanContent)) !== null) {
        thinkBlockCount++
        // 添加 think 标签之前的内容（正文）
        if (match.index > lastIndex) {
          const beforeContent = cleanContent.substring(lastIndex, match.index)
          if (beforeContent.trim()) {
            result += this.formatTextContent(beforeContent, 'main')
          }
        }

        // 添加 think 内容
        const thinkContent = match[1]
        if (thinkContent.trim()) {
          result += this.formatTextContent(thinkContent, 'think')
        }

        lastIndex = thinkRegex.lastIndex
      }


      // 添加最后剩余的内容
      if (lastIndex < cleanContent.length) {
        const remainingContent = cleanContent.substring(lastIndex)
        if (remainingContent.trim()) {
          result += this.formatTextContent(remainingContent, 'main')
        }
      }

      return result || this.formatTextContent(cleanContent, 'main')
    },

    parseMarkdownTable(tableLines) {
      if (!tableLines || tableLines.length < 2) return null

      // 解析表格行
      const parseRow = (line) => {
        return line
          .split('|')
          .map(cell => {
            // 去除前后空格和 Markdown 粗体标记 **
            let cleanCell = cell.trim()
            // 移除粗体标记 **text** -> text（处理整个单元格被包裹的情况）
            cleanCell = cleanCell.replace(/^\*\*(.*?)\*\*$/g, '$1')
            // 移除单元格内所有的粗体标记
            cleanCell = cleanCell.replace(/\*\*(.*?)\*\*/g, '$1')
            return cleanCell
          })
          .filter((cell, idx, arr) => {
            // 过滤掉首尾的空单元格（由于 | 开头/结尾产生）
            return !(idx === 0 && cell === '') && !(idx === arr.length - 1 && cell === '')
          })
      }

      const rows = tableLines.map(line => parseRow(line))
      
      // 检查是否有分隔符行（第二行通常是 |---|---|---| 格式）
      let hasHeaderSeparator = false
      let headerRowIndex = 0
      let dataStartIndex = 1
      
      if (rows.length >= 2) {
        const secondRow = rows[1]
        // 检查是否为分隔符行（包含 - 或 : 字符）
        if (secondRow.every(cell => /^:?-+:?$/.test(cell))) {
          hasHeaderSeparator = true
          dataStartIndex = 2
        }
      }

      // 如果所有行的列数不一致，可能不是有效的表格
      const columnCount = rows[0].length
      const validRows = rows.filter(row => row.length === columnCount || /^:?-+:?$/.test(row[0]))
      
      if (validRows.length < 2) return null

      // 构建HTML表格
      let tableHtml = '<div class="markdown-table-wrapper"><table class="markdown-table">'
      
      // 表头
      if (hasHeaderSeparator) {
        tableHtml += '<thead><tr>'
        rows[headerRowIndex].forEach(cell => {
          tableHtml += `<th>${cell}</th>`
        })
        tableHtml += '</tr></thead>'
      }
      
      // 表体
      tableHtml += '<tbody>'
      for (let i = dataStartIndex; i < rows.length; i++) {
        // 跳过分隔符行
        if (rows[i].every(cell => /^:?-+:?$/.test(cell))) continue
        
        // 只处理列数匹配的行
        if (rows[i].length !== columnCount) continue
        
        tableHtml += '<tr>'
        rows[i].forEach((cell, cellIdx) => {
          // 如果没有表头，第一行也作为数据行
          const tag = !hasHeaderSeparator && i === 0 ? 'th' : 'td'
          tableHtml += `<${tag}>${cell}</${tag}>`
        })
        tableHtml += '</tr>'
      }
      tableHtml += '</tbody></table></div>'
      
      return tableHtml
    },

    formatTextContent(text, type = 'main') {
      if (!text) return ''

      // 思考内容：移除重复段落，避免重复占用空间
      if (type === 'think') {
        const paragraphs = text
          .split(/\n{2,}/)
          .map(p => p.trim())
          .filter(Boolean)
        const seen = new Set()
        const deduped = []
        for (const p of paragraphs) {
          const key = p.replace(/\s+/g, ' ')
          if (!seen.has(key)) {
            seen.add(key)
            deduped.push(p)
          }
        }
        text = deduped.join('\n\n')
      }

      // 0. 移除可能残留的 think 标签
      let formatted = text
        .replace(/<\/?think>/gi, '')

      // 1. 先提取并保存 HTML 标签，避免被转义
      const htmlPlaceholders = []
      // 保存文件链接包装器（包含所有嵌套的标签）
      formatted = formatted.replace(/<div class="file-link-wrapper">[\s\S]*?<\/div>/g, (match) => {
        const index = htmlPlaceholders.length
        htmlPlaceholders.push(match)
        console.log(`📎 保护文件链接 HTML [${index}]:`, match.substring(0, 100))
        return `__HTML_PLACEHOLDER_${index}__`
      })
      // 保存图片标签
      formatted = formatted.replace(/<img[^>]*>/g, (match) => {
        const index = htmlPlaceholders.length
        htmlPlaceholders.push(match)
        return `__HTML_PLACEHOLDER_${index}__`
      })
      // 保存单独的链接标签
      formatted = formatted.replace(/<a[^>]*>.*?<\/a>/g, (match) => {
        const index = htmlPlaceholders.length
        htmlPlaceholders.push(match)
        return `__HTML_PLACEHOLDER_${index}__`
      })

      // 2. 处理代码块（在转义前处理）
      const codeBlockPlaceholders = []
      formatted = formatted.replace(/```(\w+)?\n?([\s\S]*?)```/g, (match, lang, code) => {
        const index = codeBlockPlaceholders.length
        const escapedCode = code.trim()
          .replace(/&/g, '&amp;')
          .replace(/</g, '&lt;')
          .replace(/>/g, '&gt;')
        codeBlockPlaceholders.push(`<pre class="code-block"><code>${escapedCode}</code></pre>`)
        return `__CODE_BLOCK_${index}__`
      })

      // 3. 处理行内代码（在转义前处理）
      const inlineCodePlaceholders = []
      formatted = formatted.replace(/`([^`]+)`/g, (match, code) => {
        const index = inlineCodePlaceholders.length
        const escapedCode = code
          .replace(/&/g, '&amp;')
          .replace(/</g, '&lt;')
          .replace(/>/g, '&gt;')
        inlineCodePlaceholders.push(`<code class="inline-code">${escapedCode}</code>`)
        return `__INLINE_CODE_${index}__`
      })

      // 4. 转义普通文本中的 HTML 标签
      formatted = formatted
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')

      // 4.1 精简空行：去掉首尾空行并合并多余空行
      formatted = formatted
        .replace(/^\n+/, '')
        .replace(/\n+$/, '')
        .replace(/\n{2,}/g, '\n')

      // 4.2 处理 Markdown 表格（在换行替换之前处理）
      const tablePlaceholders = []
      const lines = formatted.split('\n')
      let i = 0
      while (i < lines.length) {
        const line = lines[i].trim()
        // 检测表格行（包含 | 符号）
        if (line.includes('|') && line.split('|').length >= 3) {
          const tableLines = []
          let j = i
          // 收集连续的表格行
          while (j < lines.length && lines[j].trim().includes('|')) {
            tableLines.push(lines[j].trim())
            j++
          }
          
          // 如果至少有2行（表头+分隔符或表头+数据行），则认为是表格
          if (tableLines.length >= 2) {
            const tableHtml = this.parseMarkdownTable(tableLines)
            if (tableHtml) {
              const index = tablePlaceholders.length
              tablePlaceholders.push(tableHtml)
              // 将表格行替换为占位符，并标记要删除的行
              lines[i] = `__TABLE_PLACEHOLDER_${index}__`
              // 删除表格的其他行（从后往前删除）
              for (let k = j - 1; k > i; k--) {
                lines.splice(k, 1)
              }
              i++
              continue
            }
          }
        }
        i++
      }
      formatted = lines.join('\n')

      // 5. 处理 Markdown 语法
      // 处理粗体
      formatted = formatted.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')

      // 处理换行
      formatted = formatted.replace(/\n/g, '<br style="line-height: 1.6; margin: 0; padding: 0;">')

      // 恢复表格占位符
      tablePlaceholders.forEach((table, index) => {
        formatted = formatted.replace(`__TABLE_PLACEHOLDER_${index}__`, table)
      })

      // 处理列表
      formatted = formatted.replace(/^- (.+)(<br[^>]*>)/gm, '<li>$1</li>$2')
      formatted = formatted.replace(/(<li>.*<\/li>(<br[^>]*>)?)+/g, '<ul>$&</ul>')

      // 6. 恢复占位符
      // 恢复代码块
      codeBlockPlaceholders.forEach((code, index) => {
        formatted = formatted.replace(`__CODE_BLOCK_${index}__`, code)
      })

      // 恢复行内代码
      inlineCodePlaceholders.forEach((code, index) => {
        formatted = formatted.replace(`__INLINE_CODE_${index}__`, code)
      })

      // 恢复 HTML 标签
      htmlPlaceholders.forEach((html, index) => {
        if (html.includes('file-link-wrapper')) {
          console.log(`📎 恢复文件链接 HTML [${index}]:`, html.substring(0, 100))
        }
        formatted = formatted.replace(`__HTML_PLACEHOLDER_${index}__`, html)
      })

      // 根据类型包装内容
      if (type === 'think') {
        // 生成唯一的 ID
        const thinkId = `think-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
        return `<div class="think-block">
          <div class="think-header" onclick="toggleThinkBlock('${thinkId}')">
            <i class="el-icon-view"></i>
            <span>思考过程</span>
            <i class="el-icon-arrow-down think-toggle-icon" id="icon-${thinkId}"></i>
          </div>
          <div class="think-content collapsed" id="content-${thinkId}">${formatted}</div>
        </div>`
      } else {
        return `<div class="main-content">${formatted}</div>`
      }
    },

    async startNewChat() {
      // 如果正在加载，先停止
      if (this.isLoading) {
        await this.stopGeneration()
      }

      this.currentConversationId = null
      this.messages = []
      this.attachments = []
      this.currentTaskId = null
      this.abortController = null
      this.streamController = null
      this.currentSuggestedQuestions = []
      this.conversationInputs = {} // 清空保存的变量

      // 重置变量值为默认值
      const resetValues = {}
      this.appConfig.variables.forEach(v => {
        const varName = v.variable || v.name
        if (v.type === 'file' || v.type === 'file-list') {
          resetValues[varName] = v.type === 'file-list' ? [] : null
        } else if (v.type === 'select') {
          resetValues[varName] = v.default || (v.options && v.options[0]) || ''
        } else {
          resetValues[varName] = v.default || ''
        }
      })
      this.variableValues = resetValues

      this.$message.success('已创建新会议记录')
    },

    async loadConversation(conversationId) {
      console.log('🔄 loadConversation 被调用，conversationId:', conversationId, 'isLoadingHistory:', this.isLoadingHistory)

      // 如果正在加载历史记录，直接返回
      if (this.isLoadingHistory) {
        console.log('⚠️ 正在加载历史记录，跳过此次请求')
        return
      }

      // 如果正在生成消息，先停止
      if (this.isLoading) {
        await this.stopGeneration()
      }

      // 如果点击的是当前对话，不重复加载
      if (this.currentConversationId === conversationId && this.messages.length > 0) {
        console.log('⚠️ 已经是当前对话，跳过加载')
        return
      }

      this.isLoadingHistory = true
      this.currentConversationId = conversationId
      this.messages = []
      this.attachments = []
      this.currentSuggestedQuestions = []
      this.conversationInputs = {} // 清空保存的变量（加载历史会话时无法恢复）

      // 显示加载提示
      const loading = this.$loading({
        lock: true,
        text: '加载对话历史中...',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.7)'
      })

      try {
        // 获取历史消息
        const result = await getMessages(conversationId, this.userId, 50, this.appName)

        console.log('📥 API 返回的历史消息数量:', result?.data?.length)

        if (result && result.data && result.data.length > 0) {
          // API 返回的是倒序（最新的在前），需要反转为正序（最旧的在前）
          // 按 created_at 排序，从旧到新
          const historyData = [...result.data].sort((a, b) => a.created_at - b.created_at)

          console.log('📊 开始处理 %d 条历史消息', historyData.length)

          // 将每条消息转换为用户消息和AI回复（messages 已经在上面清空过了）
          historyData.forEach((msg, index) => {
            // 1. 添加用户消息
            if (msg.query) {
              // 处理用户上传的文件（从 message_files 中提取属于用户的文件）
              let uploadedFilesInfo = []
              if (msg.message_files && msg.message_files.length > 0) {
                console.log(`      📎 检测到 ${msg.message_files.length} 个 message_files:`, msg.message_files)

                // 过滤用户上传的文件
                // belongs_to 可能是 'user' 或 'assistant'，也可能不存在
                const userFiles = msg.message_files.filter(file => {
                  // 尝试多种判断方式
                  const isUserFile =
                    file.belongs_to === 'user' ||
                    file.created_by_role === 'user' ||
                    (!file.belongs_to && !file.created_by_role) // 如果没有明确标记，也认为是用户的

                  console.log(`        文件 ${file.name || file.filename}:`, {
                    belongs_to: file.belongs_to,
                    created_by_role: file.created_by_role,
                    isUserFile: isUserFile
                  })

                  return isUserFile
                })

                uploadedFilesInfo = userFiles.map(file => ({
                  name: file.name || file.filename || '文件',
                  size: file.size || 0,
                  url: file.url
                }))

                console.log(`      ✅ 提取到 ${uploadedFilesInfo.length} 个用户文件`)
              }

              const userMsg = {
                role: 'user',
                content: msg.query,
                created_at: msg.created_at * 1000,
                messageId: `${msg.id}-user`  // 添加后缀区分用户消息
              }

              // 如果有文件信息，添加到消息对象中
              if (uploadedFilesInfo.length > 0) {
                userMsg.uploadedFiles = uploadedFilesInfo
                console.log(`      ✅✅✅ 用户消息包含 ${uploadedFilesInfo.length} 个文件:`, uploadedFilesInfo)
              }

              this.messages.push(userMsg)
              console.log(`  [${index + 1}] ➡️ 用户: ${msg.query.substring(0, 30)}...`, uploadedFilesInfo.length > 0 ? `(${uploadedFilesInfo.length}个文件)` : '')
              console.log(`      完整消息对象:`, JSON.stringify(userMsg, null, 2))
            }

            // 2. 构建 AI 回复内容
            let assistantContent = ''

            const answerHasThinkTags = msg.answer && msg.answer.includes('<think>')
            const hasAgentThoughts = Array.isArray(msg.agent_thoughts) && msg.agent_thoughts.length > 0

            // 【核心逻辑优化】
            // 1. 优先使用 Dify API 返回的结构化数据（agent_thoughts）
            // 2. 只在 answer 中没有 <think> 标签时，才从 agent_thoughts 构建思考块
            // 3. 不再依赖固定关键词判断，完全基于 API 返回的数据结构

            if (!answerHasThinkTags && hasAgentThoughts) {
              console.log(`      ✅ 从 agent_thoughts 构建 ${msg.agent_thoughts.length} 个思考块`)
              msg.agent_thoughts.forEach((thought, idx) => {
                if (thought.thought) {
                  let thinkContent = `**分析过程 ${idx + 1}:**\n${thought.thought.trim()}`

                  if (thought.tool) {
                    thinkContent += `\n\n**使用工具:** ${thought.tool}`
                  }
                  if (thought.observation) {
                    thinkContent += `\n\n**执行结果:** ${thought.observation}`
                  }

                  // 统一使用 <think> 标签包裹思考内容
                  assistantContent += `<think>${thinkContent}</think>`
                }
              })
            } else if (answerHasThinkTags) {
              console.log(`      ℹ️ answer 中已包含 <think> 标签，使用开发者自定义的思考内容`)
            }

            // 2.3 添加 answer 内容
            if (msg.answer && msg.answer.trim()) {
              // 如果没有 agent_thoughts 且没有 <think> 标签，尝试智能检测
              if (!answerHasThinkTags && !hasAgentThoughts && this.detectThinkingContent(msg.answer)) {
                const { thinkingPart, answerPart } = this.extractThinkingContent(msg.answer)
                if (thinkingPart) {
                  assistantContent += `<think>${thinkingPart}</think>`
                  console.log('      ✅ 智能识别到历史消息中的思考过程')
                  if (answerPart) {
                    assistantContent += answerPart
                  }
                } else {
                  assistantContent += msg.answer
                }
              } else {
                assistantContent += msg.answer
              }
              console.log(`      ⬅️ AI 回答: ${msg.answer.substring(0, 30)}... (${msg.answer.length}字)`)
            } else {
              console.log(`      ⚠️ 没有 answer 内容`)
            }

            // 2.3 如果有消息文件（只处理 AI 生成的文件，用户上传的文件已在用户消息中显示）
            if (msg.message_files && msg.message_files.length > 0) {
              // 只显示 AI 生成的文件（assistant 的文件）
              const assistantFiles = msg.message_files.filter(file =>
                file.belongs_to === 'assistant' ||
                file.created_by_role === 'assistant'
              )

              if (assistantFiles.length > 0) {
                assistantContent += '\n\n'
                assistantFiles.forEach((file, fileIdx) => {
                  console.log(`      📎 AI 文件 ${fileIdx + 1}:`, JSON.stringify(file, null, 2))

                if (file.type === 'image') {
                  assistantContent += `\n<img src="${file.url}" alt="生成的图片" style="max-width: 100%; border-radius: 8px; margin-top: 8px;" />`
                } else {
                  // 尝试多种方式获取文件名
                  let fileName = file.name || file.filename || file.title

                  // 尝试从 belongs_to 字段获取
                  if (!fileName && file.belongs_to) {
                    fileName = file.belongs_to.name || file.belongs_to.filename
                  }

                  // 尝试从 extension_data 获取
                  if (!fileName && file.extension_data) {
                    fileName = file.extension_data.name || file.extension_data.filename
                  }

                  // 尝试从 URL 查询参数中获取（Dify 可能在 URL 中包含文件名）
                  if (!fileName && file.url) {
                    try {
                      const urlObj = new URL(file.url)
                      // 检查查询参数
                      const nameParam = urlObj.searchParams.get('name') ||
                                       urlObj.searchParams.get('filename') ||
                                       urlObj.searchParams.get('file_name')
                      if (nameParam) {
                        fileName = decodeURIComponent(nameParam)
                      } else {
                        // 尝试从路径提取
                        const pathParts = urlObj.pathname.split('/')
                        const lastPart = pathParts[pathParts.length - 1]
                        if (lastPart && lastPart !== 'file-preview') {
                          fileName = decodeURIComponent(lastPart)
                        }
                      }
                    } catch (e) {
                      console.warn('无法从 URL 提取文件名:', e)
                    }
                  }

                  // 最后的回退选项
                  if (!fileName || fileName === 'file-preview') {
                    fileName = `${file.type || 'document'}文件`
                  }

                  console.log(`      📎 最终使用的文件名: ${fileName}`)
                  assistantContent += `\n<div class="file-link-wrapper"><i class="el-icon-document"></i> <a href="${file.url}" target="_blank" class="file-link">${fileName}</a></div>`
                  }
                })
              }
            }

            // 3. 添加 AI 回复消息（只有当有内容时才添加）
            if (assistantContent.trim()) {
              this.messages.push({
                role: 'assistant',
                content: assistantContent,
                created_at: msg.created_at * 1000 + 1,  // 稍微偏移时间戳，避免与用户消息完全相同
                messageId: `${msg.id}-assistant`,  // 添加后缀区分 AI 消息
                hasAgentThoughts: Boolean(msg.agent_thoughts),
                hasFiles: msg.message_files && msg.message_files.length > 0
              })
            }
          })

          console.log('✅ 历史记录加载完成，共 %d 条消息（%d 轮对话）', this.messages.length, historyData.length)

          // 滚动到底部并自动折叠思考过程
          this.$nextTick(() => {
            this.scrollToBottom()
            this.autoCollapseThinkBlocks()
          })

          this.$message.success(`已加载 ${historyData.length} 轮对话`)
        } else {
          this.$message.info('该对话暂无历史记录')
        }
      } catch (error) {
        console.error('加载对话历史失败:', error)
        this.$message.error('加载对话历史失败：' + (error.message || '未知错误'))
      } finally {
        loading.close()
        this.isLoadingHistory = false  // 重置加载状态
      }
    },

    async loadConversationsList() {
      try {
        const result = await getConversations(this.userId, 50, this.appName)
        this.conversations = result.data || []
      } catch (error) {
        console.error('加载会话列表失败:', error)
      }
    },

    async deleteChat(conversationId) {
      try {
        await this.$confirm('确定要删除这条对话记录吗？', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        })

        await deleteConversation(conversationId, this.userId)

        if (this.currentConversationId === conversationId) {
          this.startNewChat()
        }

        await this.loadConversationsList()
        this.$message.success('已删除')
      } catch (error) {
        if (error !== 'cancel') {
          this.$message.error('删除失败')
        }
      }
    },

    // Chatflow 文件上传
    triggerChatflowFileUpload() {
      if (this.$refs.chatflowFile) {
        this.$refs.chatflowFile.click()
      }
    },

    handleChatflowFileUpload(e) {
      const files = Array.from((e && e.target && e.target.files) || [])
      if (!files.length) return

      const mapped = files.map(f => ({
        uid: `${Date.now()}_${Math.random().toString(36).slice(2)}`,
        name: f.name,
        size: f.size,
        raw: f
      }))

      this.attachments = this.attachments.concat(mapped)
      this.$message.success(`已选择 ${files.length} 个文件`)

      if (this.$refs.chatflowFile) {
        this.$refs.chatflowFile.value = ''
      }
    },

    removeAttachment(index) {
      if (index < 0 || index >= this.attachments.length) return
      const removed = this.attachments.splice(index, 1)[0]
      if (removed) {
        this.$message.success('已移除：' + (removed.name || '文件'))
      }
    },

    getFileType(filename) {
      const ext = (filename.split('.').pop() || '').toLowerCase()
      const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg']
      const docExts = ['txt', 'md', 'pdf', 'doc', 'docx', 'html', 'xlsx', 'xls', 'csv']

      if (imageExts.includes(ext)) return 'image'
      if (docExts.includes(ext)) return 'document'
      return 'custom'
    },

    formatSize(bytes) {
      if (bytes < 1024) return bytes + 'B'
      const kb = bytes / 1024
      if (kb < 1024) return kb.toFixed(2) + 'KB'
      const mb = kb / 1024
      return mb.toFixed(2) + 'MB'
    },

    formatTime(timestamp) {
      const date = new Date(timestamp)
      const now = new Date()
      const diff = now - date

      if (diff < 60000) return '刚刚'
      if (diff < 3600000) return Math.floor(diff / 60000) + '分钟前'
      if (diff < 86400000) return Math.floor(diff / 3600000) + '小时前'

      return date.toLocaleString('zh-CN', {
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit'
      })
    },

    scrollToBottom() {
      if (this.$refs.messagesContainer) {
        this.$refs.messagesContainer.scrollTop = this.$refs.messagesContainer.scrollHeight
      }
    },

    autoCollapseThinkBlocks() {
      // 自动折叠所有思考过程
      this.$nextTick(() => {
        const expandedThinkBlocks = document.querySelectorAll('.think-content:not(.collapsed)')

        if (expandedThinkBlocks.length > 0) {
          console.log(`🔽 折叠 ${expandedThinkBlocks.length} 个思考块`)
          expandedThinkBlocks.forEach(block => {
            const id = block.id.replace('content-', '')
            if (id) {
              window.toggleThinkBlock(id)
            }
          })
        }
      })
    },

    autoResizeTextarea() {
      if (this.$refs.textarea) {
        this.$refs.textarea.style.height = 'auto'
        this.$refs.textarea.style.height = this.$refs.textarea.scrollHeight + 'px'
      }
    },

    setQuickMessage(msg) {
      this.message = msg
      this.$nextTick(() => {
        if (this.$refs.textarea) {
          this.$refs.textarea.focus()
        }
      })
    },

    async rethinkMessage(messageIndex) {
      // 找到这个 AI 回答对应的用户问题
      const aiMessage = this.messages[messageIndex]
      if (aiMessage.role !== 'assistant') {
        this.$message.error('只能重新思考 AI 的回答')
        return
      }

      // 向前查找最近的用户消息
      let userMessage = null
      for (let i = messageIndex - 1; i >= 0; i--) {
        if (this.messages[i].role === 'user') {
          userMessage = this.messages[i]
          break
        }
      }

      if (!userMessage) {
        this.$message.error('未找到对应的用户问题')
        return
      }

      // 确认操作
      try {
        await this.$confirm('确定要重新思考这个问题吗？将删除当前回答并重新生成。', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        })
      } catch {
        return
      }

      // 删除 AI 回答
      this.messages.splice(messageIndex, 1)

      // 重新发送用户问题
      this.message = userMessage.content
      await this.$nextTick()
      this.handleSend()
    },

    copyMessage(content) {
      // 移除 HTML 标签和 <think> 标签，只复制纯文本
      let textContent = content
        // 移除 <think> 标签及其内容
        .replace(/<think>[\s\S]*?<\/think>/gi, '')
        // 移除 HTML 标签
        .replace(/<[^>]+>/g, '')
        // 将 HTML 实体转回普通字符
        .replace(/&lt;/g, '<')
        .replace(/&gt;/g, '>')
        .replace(/&amp;/g, '&')
        // 将 <br> 转为换行
        .replace(/<br\s*\/?>/gi, '\n')
        // 移除多余的空白
        .trim()

      // 复制到剪贴板
      if (navigator.clipboard && navigator.clipboard.writeText) {
        navigator.clipboard.writeText(textContent)
          .then(() => {
            this.$message.success('已复制到剪贴板')
          })
          .catch(() => {
            // 降级方案
            this.fallbackCopy(textContent)
          })
      } else {
        // 降级方案
        this.fallbackCopy(textContent)
      }
    },

    fallbackCopy(text) {
      const textarea = document.createElement('textarea')
      textarea.value = text
      textarea.style.position = 'fixed'
      textarea.style.opacity = '0'
      document.body.appendChild(textarea)
      textarea.select()
      try {
        document.execCommand('copy')
        this.$message.success('已复制到剪贴板')
      } catch (err) {
        this.$message.error('复制失败，请手动复制')
      }
      document.body.removeChild(textarea)
    },

    resendMessage(content) {
      if (this.isLoading) {
        this.$message.warning('请等待当前消息完成')
        return
      }

      // 直接设置到输入框
      this.message = content
      this.$nextTick(() => {
        if (this.$refs.textarea) {
          this.$refs.textarea.focus()
        }
      })
    },

    /**
     * 处理变量文件选择（单文件变量）
     */
    handleVariableFileSelect(variable, e) {
      const files = Array.from((e && e.target && e.target.files) || [])
      if (!files.length) return

      const varName = variable.variable || variable.name
      const file = files[0]

      // 检查 .doc 文件
      if (file.name.toLowerCase().endsWith('.doc') && !file.name.toLowerCase().endsWith('.docx')) {
        this.$message.error('不支持 .doc 格式文件，请使用 .docx 格式')
        if (e.target) {
          e.target.value = ''
        }
        return
      }

      this.$set(this.variableValues, varName, {
        uid: `${Date.now()}_${Math.random().toString(36).slice(2)}`,
        name: file.name,
        size: file.size,
        raw: file
      })

      this.$message.success(`已选择文件：${file.name}`)
      if (e.target) {
        e.target.value = ''
      }
    },

    /**
     * 处理变量文件列表选择（文件列表变量）
     */
    handleVariableFileListSelect(variable, e) {
      const files = Array.from((e && e.target && e.target.files) || [])
      if (!files.length) return

      const varName = variable.variable || variable.name

      // 检查 .doc 文件
      const docFiles = files.filter(f => {
        const name = f.name.toLowerCase()
        return name.endsWith('.doc') && !name.endsWith('.docx')
      })
      if (docFiles.length > 0) {
        this.$message.error('不支持 .doc 格式文件，请使用 .docx 格式。不支持的文件：' + docFiles.map(f => f.name).join('、'))
        if (e.target) {
          e.target.value = ''
        }
        return
      }

      const mapped = files.map(f => ({
        uid: `${Date.now()}_${Math.random().toString(36).slice(2)}`,
        name: f.name,
        size: f.size,
        raw: f
      }))

      // 添加到现有列表
      if (!this.variableValues[varName]) {
        this.$set(this.variableValues, varName, [])
      }
      this.$set(this.variableValues, varName, this.variableValues[varName].concat(mapped))

      this.$message.success(`已添加 ${files.length} 个文件`)
      if (e.target) {
        e.target.value = ''
      }
    },

    /**
     * 移除变量文件（单文件变量）
     */
    removeVariableFile(variable) {
      const varName = variable.variable || variable.name
      this.$set(this.variableValues, varName, null)
      this.$message.success('已移除文件')
    },

    /**
     * 移除变量文件列表中的某个文件
     */
    removeVariableFileFromList(variable, index) {
      const varName = variable.variable || variable.name
      if (this.variableValues[varName] && Array.isArray(this.variableValues[varName])) {
        const removed = this.variableValues[varName].splice(index, 1)[0]
        if (removed) {
          this.$message.success(`已移除：${removed.name}`)
        }
      }
    },

    /**
     * 智能检测 answer 中是否包含思考过程
     * @param {string} content - 要检测的内容
     * @returns {boolean} 是否包含思考过程
     */
    detectThinkingContent(content) {
      if (!content || typeof content !== 'string') return false

      // 检测思考过程的特征模式
      const thinkingPatterns = [
        // 中文思考模式
        /(?:我需要|我将|让我|首先|接下来|然后|最后|总结|根据|基于|通过|分析|检查|提取|确认|注意|强调)/,
        // 英文思考模式
        /(?:I need to|I will|Let me|First|Next|Then|Finally|Based on|According to|Through|By analyzing)/,
        // 步骤性描述
        /(?:步骤|步骤一|步骤二|第一步|第二步|首先|其次|最后)/,
        // 分析性描述
        /(?:分析|检查|提取|确认|验证|评估|考虑|思考)/,
        // 任务性描述
        /(?:任务|要求|需要|应该|必须|确保|保证)/
      ]

      // 检测是否包含多个思考特征
      const thinkingCount = thinkingPatterns.filter(pattern => pattern.test(content)).length

      // 检测内容长度（思考过程通常比较长）
      const isLongContent = content.length > 200

      // 检测是否包含结构化内容（如标题、列表等）
      const hasStructure = /(?:#|##|###|####|\*\*|\*|一、|二、|三、|（一）|（二）)/.test(content)

      // 综合判断：包含多个思考特征 且 (内容较长 或 有结构化内容)
      return thinkingCount >= 2 && (isLongContent || hasStructure)
    },

    /**
     * 从内容中提取思考过程和回答部分
     * @param {string} content - 原始内容
     * @returns {Object} { thinkingPart, answerPart }
     */
    extractThinkingContent(content) {
      if (!content) return { thinkingPart: null, answerPart: content }

      // 尝试多种分离策略

      // 策略1：基于标题分离（如 # 执法检查通知书）
      const titleMatch = content.match(/^(.*?)(#\s*[^#\n]+.*)$/s)
      if (titleMatch) {
        const thinkingPart = titleMatch[1].trim()
        const answerPart = titleMatch[2].trim()

        if (thinkingPart.length > 50 && this.detectThinkingContent(thinkingPart)) {
          return { thinkingPart, answerPart }
        }
      }

      // 策略2：基于分隔符分离（如 --- 或 ===）
      const separatorMatch = content.match(/^(.*?)(?:---|===|___)(.*)$/s)
      if (separatorMatch) {
        const thinkingPart = separatorMatch[1].trim()
        const answerPart = separatorMatch[2].trim()

        if (thinkingPart.length > 50 && this.detectThinkingContent(thinkingPart)) {
          return { thinkingPart, answerPart }
        }
      }

      // 策略3：基于内容特征分离
      // 查找第一个明显的"结果"或"输出"部分
      const resultPatterns = [
        /(.*?)(#\s*[^#\n]+.*)$/s,  // 标题开始
        /(.*?)(##\s*[^#\n]+.*)$/s, // 二级标题开始
        /(.*?)(\*\*[^*]+\*\*.*)$/s, // 粗体开始
        /(.*?)(一、[^一]*二、.*)$/s  // 中文序号开始
      ]

      for (const pattern of resultPatterns) {
        const match = content.match(pattern)
        if (match) {
          const thinkingPart = match[1].trim()
          const answerPart = match[2].trim()

          if (thinkingPart.length > 50 && this.detectThinkingContent(thinkingPart)) {
            return { thinkingPart, answerPart }
          }
        }
      }

      // 策略4：基于长度和内容特征智能分离
      const lines = content.split('\n')
      let thinkingLines = []
      let answerLines = []
      let foundAnswerStart = false

      for (let i = 0; i < lines.length; i++) {
        const line = lines[i]

        // 检测是否是答案开始的标志
        if (!foundAnswerStart && (
          line.match(/^#\s/) ||  // 标题
          line.match(/^##\s/) || // 二级标题
          line.match(/^\*\*[^*]+\*\*$/) || // 粗体标题
          line.match(/^[一二三四五六七八九十]+、/) || // 中文序号
          line.match(/^[1-9]+\./) // 数字序号
        )) {
          foundAnswerStart = true
          answerLines.push(line)
        } else if (foundAnswerStart) {
          answerLines.push(line)
        } else {
          thinkingLines.push(line)
        }
      }

      const thinkingPart = thinkingLines.join('\n').trim()
      const answerPart = answerLines.join('\n').trim()

      if (thinkingPart.length > 50 && answerPart.length > 20 && this.detectThinkingContent(thinkingPart)) {
        return { thinkingPart, answerPart }
      }

      // 如果无法分离，返回原内容
      return { thinkingPart: null, answerPart: content }
    },

    /**
     * 预览文件
     */
    previewFile(file) {
      if (!file) return

      // 如果有 URL，直接在新窗口打开
      if (file.url) {
        window.open(file.url, '_blank')
      } else if (file.raw) {
        // 如果有原始文件，创建临时 URL 并打开
        const url = URL.createObjectURL(file.raw)
        window.open(url, '_blank')
        // 延迟释放 URL（给浏览器时间打开）
        setTimeout(() => URL.revokeObjectURL(url), 60000)
      } else {
        this.$message.warning('文件预览不可用')
      }
    },

    toggleTheme() {
      // 主题切换功能（可选）
      console.log('主题切换')
    },

    handleLogin() {
      // 登录功能（可选）
      this.$router.push('/login')
    }
  },
  watch: {
    message() {
      this.$nextTick(() => {
        this.autoResizeTextarea()
      })
    }
  }
}
</script>

<style lang="scss" scoped>
.page-wrapper {
  width: 100%;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.home-root {
  position: relative;
  width: 100%;
  height: calc(100% - 66px); /* 减去导航栏高度 */
  margin-top: 66px; /* 为固定导航栏留出空间 */
  display: flex;
  flex-direction: row;
  background: linear-gradient(135deg, #f5f7ff 0%, #f0f4ff 100%);
  --c-primary: #3b82f6;
  --c-primary-2: #2563eb;
  --c-accent: #0ea5e9;
  --c-bg: #f1f5f9;
  --c-card: #ffffff;
  --c-border: #cbd5e1;
  --c-muted: #64748b;
  --c-law-blue: #3b82f6;
  --c-law-dark: #2563eb;
  --c-law-gray: #475569;
}

.home-root::before {
  content: '';
  position: absolute;
  top: 15%;
  left: 15%;
  width: 320px;
  height: 320px;
  background: linear-gradient(135deg, rgba(0, 82, 204, 0.7), rgba(0, 122, 255, 0.5));
  border-radius: 50%;
  filter: blur(80px);
  opacity: 0.35;
  z-index: 0;
  animation: float 20s infinite alternate ease-in-out;
  pointer-events: none;
}

.home-root::after {
  content: '';
  position: absolute;
  bottom: 15%;
  right: 15%;
  width: 240px;
  height: 240px;
  background: linear-gradient(135deg, rgba(0, 102, 255, 0.6), rgba(0, 178, 255, 0.4));
  border-radius: 50%;
  filter: blur(80px);
  opacity: 0.35;
  z-index: 0;
  animation: float 20s infinite alternate ease-in-out;
  animation-delay: -5s;
  pointer-events: none;
}

@keyframes float {
  0% {
    transform: translate(0, 0) scale(1);
  }
  100% {
    transform: translate(50px, 30px) scale(1.1);
  }
}

.home-root > .sidebar,
.home-root > .main-content {
  position: relative;
  z-index: 1;
}

/* 主内容区样式 */
.main-content {
  flex: 1;
  height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.welcome-area {
  flex: 1;
  min-height: 300px;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  background: rgba(255, 255, 255, 0.25);
}

.welcome-area::before {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(255, 255, 255, 0);
  z-index: 0;
  pointer-events: none;
}

.welcome-content {
  text-align: center;
  max-width: 600px;
  padding: 16px;
  padding-top: 68px;
  position: relative;
  z-index: 1;
}

.welcome-icon {
  width: 80px;
  height: 80px;
  margin: 0 auto 24px;
  background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 8px 24px rgba(59, 130, 246, 0.3);
  border: 3px solid rgba(59, 130, 246, 0.1);
}

.welcome-icon i {
  font-size: 40px;
  color: #ffffff;
}

.title {
  font-size: 32px;
  color: #111827;
  font-weight: 700;
  margin-bottom: 12px;
  background: linear-gradient(135deg, var(--c-law-blue) 0%, var(--c-law-gray) 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.subtitle {
  font-size: 16px;
  color: #6b7280;
  line-height: 1.6;
  margin-bottom: 32px;
}

/* 文件变量提示 */
.file-variable-tip {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 20px;
  background: linear-gradient(135deg, #dbeafe 0%, #e0f2fe 100%);
  border-left: 4px solid var(--c-law-blue);
  border-radius: 6px;
  margin: 16px auto;
  max-width: 600px;
  color: #334155;
  font-size: 14px;
  box-shadow: 0 2px 8px rgba(59, 130, 246, 0.12);
}

.file-variable-tip i {
  font-size: 18px;
  color: #dc2626;
  flex-shrink: 0;
}

/* 推荐问题（欢迎界面）*/
.suggested-questions {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
  max-width: 600px;
  margin: 0 auto;
}

.suggested-question-card {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 20px;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: 1px solid rgba(0, 0, 0, 0.06);
  border-radius: 14px;
  cursor: pointer;
  transition: all 0.3s ease;
  text-align: left;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05);
}

.suggested-question-card:hover {
  background: rgba(255, 255, 255, 1);
  transform: translateY(-4px);
  border-color: rgba(13, 71, 161, 0.2);
  box-shadow: 0 6px 14px rgba(0, 0, 0, 0.1);
}

.suggested-question-card i {
  font-size: 20px;
  color: var(--c-law-blue);
  flex-shrink: 0;
}

.suggested-question-card span {
  font-size: 15px;
  color: #374151;
  font-weight: 500;
  line-height: 1.5;
}

/* 快捷操作（默认）*/
.quick-actions {
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
}

.quick-action-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 20px 24px;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: 1px solid rgba(0, 0, 0, 0.06);
  border-radius: 14px;
  cursor: pointer;
  transition: all 0.3s ease;
  min-width: 120px;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05);
}

.quick-action-card:hover {
  background: rgba(255, 255, 255, 1);
  transform: translateY(-6px);
  border-color: rgba(13, 71, 161, 0.2);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.12);
}

.quick-action-card i {
  font-size: 28px;
  color: var(--c-law-blue);
}

.quick-action-card span {
  font-size: 14px;
  color: #374151;
  font-weight: 500;
}

/* 聊天区域 */
.chat-area {
  flex: 1;
  min-height: 300px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  position: relative;
  background: rgba(255, 255, 255, 0.25);
}

.chat-area::before {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(255, 255, 255, 0);
  z-index: 0;
  pointer-events: none;
}

.messages-container {
  flex: 1;
  overflow-y: auto;
  padding: 68px 0 28px;
  padding-bottom: 140px; /* 为固定输入框预留空间，避免消息被遮挡 */
  display: flex;
  flex-direction: column;
  align-items: center;
  position: relative;
  z-index: 1;

  /* 自定义滚动条 */
  &::-webkit-scrollbar {
    width: 10px;
  }

  &::-webkit-scrollbar-track {
    background: transparent;
  }

  &::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.3);
    border-radius: 5px;
  }

  &::-webkit-scrollbar-thumb:hover {
    background: rgba(255, 255, 255, 0.5);
  }
}

.message-wrapper {
  display: flex;
  margin-bottom: 28px;
  animation: fadeIn 0.6s ease;
  width: 100%;
  max-width: 900px;
  padding: 0 24px;
  box-sizing: border-box;
}

.message-wrapper.user {
  justify-content: flex-end;
}

.message-wrapper.assistant {
  justify-content: flex-start;
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(15px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.message-item {
  display: flex;
  gap: 18px;
  max-width: 85%;
  min-width: 200px;
  align-items: flex-start;
  width: fit-content;
}

/* 响应式设计：超大屏幕时限制宽度 */
@media (min-width: 1400px) {
  .message-wrapper {
    max-width: 1000px;
  }

  .message-item {
    max-width: 80%;
  }
}

/* 中等屏幕 */
@media (max-width: 768px) {
  .message-wrapper {
    max-width: 100%;
    padding: 0 16px;
  }

  .message-item {
    max-width: 90%;
    min-width: auto;
  }
}

.message-avatar {
  width: 52px;
  height: 52px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  font-size: 24px;
  background: rgba(255, 255, 255, 0.75);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: 1px solid rgba(255, 255, 255, 0.85);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.08);
}

.message-wrapper.user .message-avatar {
  background: linear-gradient(135deg, rgba(13, 71, 161, 0.85), rgba(25, 118, 210, 0.7));
  color: #ffffff;
}

.message-wrapper.assistant .message-avatar {
  background: rgba(255, 255, 255, 0.75);
  color: #3b82f6;
}

.message-content {
  flex: 0 1 auto;
  min-width: 0;
  width: fit-content;
}

.message-text {
  padding: 20px 25px;
  border-radius: 24px;
  line-height: 1.75;
  font-size: 16px;
  word-wrap: break-word;
  word-break: break-word;
  transition: all 0.3s ease;
  width: fit-content;
  max-width: 100%;
  min-width: 120px;
  height: auto;
  min-height: auto;
}

.message-wrapper.user .message-text {
  background: rgba(255, 255, 255, 0.85);
  backdrop-filter: blur(14px) saturate(120%);
  -webkit-backdrop-filter: blur(14px) saturate(120%);
  color: #333;
  border-top-right-radius: 8px;
  border: 2px solid rgba(13, 71, 161, 0.15);
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.06), inset 0 0 0 1px rgba(255, 255, 255, 0.5);
}

.message-wrapper.assistant .message-text {
  background: rgba(255, 255, 255, 0.85);
  backdrop-filter: blur(14px) saturate(120%);
  -webkit-backdrop-filter: blur(14px) saturate(120%);
  color: #333;
  border: 2px solid rgba(13, 71, 161, 0.12);
  border-top-left-radius: 8px;
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.06), inset 0 0 0 1px rgba(255, 255, 255, 0.5);
}

.message-wrapper.user .message-text ::v-deep strong {
  color: #1e40af;
  font-weight: 600;
}

.message-wrapper.assistant .message-text ::v-deep strong {
  color: #111827;
  font-weight: 600;
}

.message-text ::v-deep .code-block {
  background: #f8fafc;
  color: #334155;
  padding: 20px 24px;
  border-radius: 8px;
  margin: 12px 0;
  overflow-x: auto;
  font-family: var(--font-family-code);
  font-size: var(--font-size-text-normal);
  line-height: var(--line-height-relaxed);
  border: 1px solid #e2e8f0;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}

.message-text ::v-deep .inline-code {
  background: rgba(59, 130, 246, 0.1);
  padding: 2px 6px;
  border-radius: 4px;
  font-family: var(--font-family-code);
  font-size: var(--font-size-text-small);
  color: #1e40af;
}

.message-wrapper.assistant .message-text ::v-deep .inline-code {
  background: #f1f5f9;
  color: #e11d48;
}

.message-text ::v-deep ul {
  margin: 8px 0;
  padding-left: 20px;
}

  .message-text ::v-deep li {
    margin: 4px 0;
  }

  /* Markdown 表格样式 - 现代蓝色主题 */
  .message-text ::v-deep .markdown-table-wrapper {
    margin: 12px 0;
    overflow-x: auto;
    border-radius: 12px;
    box-shadow: 0 4px 16px rgba(59, 130, 246, 0.12);
    border: 1px solid rgba(59, 130, 246, 0.1);
  }

  .message-text ::v-deep .markdown-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    background: #ffffff;
    font-size: 14px;
    line-height: 1.6;
  }

  .message-text ::v-deep .markdown-table thead {
    background-color: #3b82f6;
    color: #ffffff;
  }

  .message-text ::v-deep .markdown-table thead th {
    padding: 12px 16px;
    text-align: center;
    font-weight: 600;
    font-size: 14px;
    white-space: nowrap;
  }

  .message-text ::v-deep .markdown-table thead th:first-child {
    border-top-left-radius: 12px;
  }

  .message-text ::v-deep .markdown-table thead th:last-child {
    border-top-right-radius: 12px;
  }

  .message-text ::v-deep .markdown-table tbody tr {
    transition: all 0.3s ease;
    border-bottom: 1px solid #e5e7eb;
  }

  .message-text ::v-deep .markdown-table tbody tr:nth-child(odd) {
    background-color: #ffffff;
  }

  .message-text ::v-deep .markdown-table tbody tr:nth-child(even) {
    background-color: #f8fafc;
  }

  .message-text ::v-deep .markdown-table tbody tr:hover {
    background: linear-gradient(90deg, #eff6ff 0%, #dbeafe 100%);
    transform: scale(1.01);
    box-shadow: 0 2px 8px rgba(59, 130, 246, 0.1);
  }

  .message-text ::v-deep .markdown-table td,
  .message-text ::v-deep .markdown-table tbody th {
    padding: 12px 16px;
    border-bottom: 1px solid #e5e7eb;
    color: #1f2937;
    vertical-align: middle;
    text-align: center;
    transition: all 0.2s ease;
  }

  .message-text ::v-deep .markdown-table tbody th {
    font-weight: 600;
    background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
    color: #1e40af;
    text-align: center;
    border-right: 3px solid #3b82f6;
  }

  .message-text ::v-deep .markdown-table tbody tr:last-child td,
  .message-text ::v-deep .markdown-table tbody tr:last-child th {
    border-bottom: none;
  }

  .message-text ::v-deep .markdown-table tbody tr:last-child td:first-child {
    border-bottom-left-radius: 12px;
  }

  .message-text ::v-deep .markdown-table tbody tr:last-child td:last-child {
    border-bottom-right-radius: 12px;
  }

  /* 用户消息中的表格样式调整 */
  .message-wrapper.user .message-text ::v-deep .markdown-table-wrapper {
    box-shadow: 0 4px 16px rgba(59, 130, 246, 0.18);
    border: 1px solid rgba(59, 130, 246, 0.2);
  }

  .message-wrapper.user .message-text ::v-deep .markdown-table thead {
    background-color: #2563eb;
  }

  .message-wrapper.user .message-text ::v-deep .markdown-table tbody tr:hover {
    background: linear-gradient(90deg, #dbeafe 0%, #bfdbfe 100%);
  }

  /* 响应式表格 */
  @media (max-width: 768px) {
    .message-text ::v-deep .markdown-table-wrapper {
      font-size: 12px;
      border-radius: 8px;
    }
    
    .message-text ::v-deep .markdown-table thead th,
    .message-text ::v-deep .markdown-table td,
    .message-text ::v-deep .markdown-table tbody th {
      padding: 10px 12px;
    }
    
    .message-text ::v-deep .markdown-table tbody tr:hover {
      transform: none;
    }
  }

  /* 思考过程样式 */
  .message-text ::v-deep .think-block {
    background: #f9fafb;
    border-left: 3px solid #9ca3af;
    border-radius: 6px;
    padding: 6px 12px;
    margin-bottom: 2px;
    animation: thinkFadeIn 0.3s ease;
    font-family: var(--font-family-fangsong);
  }

@keyframes thinkFadeIn {
  from {
    opacity: 0;
    transform: translateY(-5px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.message-text ::v-deep .think-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  margin-bottom: 4px;
  font-size: var(--font-size-content-secondary);
  color: #6b7280;
  font-weight: var(--font-weight-semibold);
  cursor: pointer;
  user-select: none;
  transition: all 0.2s ease;
  padding: 2px 6px;
  border-radius: 4px;
  min-height: 24px;
}

.message-text ::v-deep .think-header:hover {
  color: #4b5563;
  background: rgba(0, 0, 0, 0.05);
}

.message-text ::v-deep .think-header i:first-child {
  font-size: 18px;
  color: #9ca3af;
}

.message-text ::v-deep .think-toggle-icon {
  font-size: 14px;
  color: #9ca3af;
  transition: transform 0.2s ease;
}

.message-text ::v-deep .think-content {
  font-size: var(--font-size-content-primary);
  line-height: var(--line-height-content);
  color: #6b7280;
  font-style: normal;
  font-family: var(--font-family-fangsong);
  padding-left: 8px;
  opacity: var(--font-opacity-muted);
  transition: all 0.3s ease;
  overflow: hidden;
}

.message-text ::v-deep .think-content.collapsed {
  max-height: 0;
  margin-bottom: 0;
  padding-top: 0;
  opacity: 0;
}

/* 用户消息中的思考块样式（如果有的话）*/
.message-wrapper.user .message-text ::v-deep .think-block {
  background: rgba(59, 130, 246, 0.08);
  border-left-color: #3b82f6;
}

.message-wrapper.user .message-text ::v-deep .think-header {
  color: #1e40af;
}

.message-wrapper.user .message-text ::v-deep .think-header:hover {
  color: #3b82f6;
  background: rgba(59, 130, 246, 0.1);
}

.message-wrapper.user .message-text ::v-deep .think-header i:first-child {
  color: #3b82f6;
  font-size: 18px;
}

.message-wrapper.user .message-text ::v-deep .think-toggle-icon {
  color: #3b82f6;
  font-size: 14px;
}

.message-wrapper.user .message-text ::v-deep .think-content {
  color: #2563eb;
  font-family: var(--font-family-fangsong);
}

/* 正文内容样式 */
.message-text ::v-deep .main-content {
  font-size: var(--font-size-content-primary);
  line-height: var(--line-height-content);
  color: #1f2937;
  font-weight: var(--font-weight-normal);
  font-family: var(--font-family-fangsong);
  margin: 0;
  padding: 0;
  height: auto !important;
  min-height: auto !important;
  max-height: none !important;
  display: block;
  overflow: visible;
}

.message-wrapper.user .message-text ::v-deep .main-content {
  color: #1e40af;
  font-family: var(--font-family-fangsong);
}

/* 强制正文内容容器高度贴合 */
.message-text ::v-deep .main-content * {
  margin: 0 !important;
  padding: 0 !important;
  height: auto !important;
  min-height: auto !important;
  max-height: none !important;
  line-height: var(--line-height-content) !important;
}

.message-text ::v-deep .main-content p {
  margin: 0 !important;
  padding: 0 !important;
  line-height: var(--line-height-content) !important;
}

.message-text ::v-deep .main-content br {
  margin: 0 !important;
  padding: 0 !important;
  line-height: var(--line-height-content) !important;
}

.message-time {
  font-size: var(--font-size-text-mini);
  color: #9ca3af;
  margin-top: 4px;
  padding: 0 4px;
}

.message-wrapper.user .message-time {
  text-align: right;
}

/* 消息操作按钮区域 */
.message-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  margin-top: 6px;
}

.action-buttons {
  display: flex;
  align-items: center;
  gap: 2px;
  opacity: 0;
  transition: opacity 0.2s;
}

.message-wrapper:hover .action-buttons {
  opacity: 1;
}

.action-btn {
  width: 28px;
  height: 28px;
  padding: 0;
  background: transparent;
  border: 1px solid #e5e7eb;
  border-radius: 6px;
  color: #6b7280;
  font-size: 14px;
  cursor: pointer;
  transition: all 0.2s;
  display: flex;
  align-items: center;
  justify-content: center;
}

.action-btn:hover:not(:disabled) {
  background: #f1f5f9;
  border-color: var(--c-law-blue);
  color: var(--c-law-blue);
  transform: translateY(-1px);
}

.action-btn:active:not(:disabled) {
  transform: translateY(0);
}

.action-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.action-btn i {
  font-size: 14px;
}

/* 变量输入面板 */
.variables-panel {
  border-top: 1px solid #e5e7eb;
  background: #ffffff;
}

.panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 24px;
  cursor: pointer;
  transition: background 0.2s;
  border-bottom: 1px solid #e5e7eb;
}

.panel-header:hover {
  background: #f9fafb;
}

.header-left {
  display: flex;
  align-items: center;
  gap: 12px;
}

.header-left i {
  font-size: 18px;
  color: var(--c-law-blue);
}

.panel-title {
  font-size: 15px;
  font-weight: 600;
  color: #374151;
}

.variable-count {
  font-size: 13px;
  color: #9ca3af;
  background: #f3f4f6;
  padding: 2px 10px;
  border-radius: 12px;
}

.panel-content {
  padding: 20px 24px;
  max-height: 200px;
  overflow-y: auto;
  background: #f9fafb;

  &::-webkit-scrollbar {
    width: 6px;
  }

  &::-webkit-scrollbar-track {
    background: transparent;
  }

  &::-webkit-scrollbar-thumb {
    background: #d1d5db;
    border-radius: 3px;
  }

  &::-webkit-scrollbar-thumb:hover {
    background: #9ca3af;
  }
}

/* 输入栏内参数弹窗内容 */
.composer-variables {
  max-height: 360px;
  overflow-y: auto;
  padding: 8px 4px;

  &::-webkit-scrollbar {
    width: 6px;
  }

  &::-webkit-scrollbar-track {
    background: transparent;
  }

  &::-webkit-scrollbar-thumb {
    background: #d1d5db;
    border-radius: 3px;
  }

  &::-webkit-scrollbar-thumb:hover {
    background: #9ca3af;
  }
}

/* 输入参数按钮强化样式 */
.parameters-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 10px;
  position: relative;
  color: #409EFF; /* 默认蓝色 */
  transition: color 0.3s ease;
}

.parameters-btn .btn-text {
  font-size: 12px;
}

.parameters-btn .badge {
  position: absolute;
  top: -6px;
  right: -6px;
  background: #ef4444;
  color: #ffffff;
  border-radius: 999px;
  height: 16px;
  min-width: 16px;
  line-height: 16px;
  text-align: center;
  font-size: 11px;
  padding: 0 4px;
}

/* 未填写必填项时显示红色 */
.parameters-btn.alert {
  color: #ef4444;
}

.variable-item {
  margin-bottom: 14px;

  &:last-child {
    margin-bottom: 0;
  }
}

.variable-label {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 8px;
  font-size: 14px;
  color: #374151;
  font-weight: 500;
}

.required-mark {
  background: #fee2e2;
  color: #dc2626;
  font-size: 12px;
  padding: 2px 8px;
  border-radius: 4px;
}

.optional-mark {
  background: #f3f4f6;
  color: #6b7280;
  font-size: 12px;
  padding: 2px 8px;
  border-radius: 4px;
}

.file-upload-area,
.file-list-upload-area {
  background: #ffffff;
  border: 1px dashed #d1d5db;
  border-radius: 8px;
  padding: 16px;
}

.upload-placeholder {
  display: flex;
  align-items: center;
  gap: 12px;
}

.upload-hint {
  font-size: 12px;
  color: #9ca3af;
}

.file-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px;
  background: #f9fafb;
  border: 1px solid #e5e7eb;
  border-radius: 8px;

  i.el-icon-document {
    font-size: 24px;
    color: var(--c-law-blue);
  }
}

.file-info {
  flex: 1;
  overflow: hidden;
}

.file-name {
  font-size: 13px;
  color: #374151;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.file-size {
  font-size: 12px;
  color: #9ca3af;
  margin-top: 2px;
}

.file-list {
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* 输入区域 - 悬浮固定样式（DeepSeek 风格）*/
.input-area {
  /* 固定在屏幕底部 */
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1000;
  /* 背景与页面一致，消除色差 */
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  padding: 20px 24px 24px;
  pointer-events: none; /* 让背景区域不阻挡点击 */
  transition: left 0.3s ease; /* 随侧边栏开合平滑过渡 */
}

/* 移除与侧边栏联动的左侧偏移，保持输入区始终居中 */

.input-wrapper {
  max-width: 800px;
  margin: 0 auto;
  pointer-events: auto; /* 恢复输入框的点击 */
}

.composer {
  width: 100%;
  background: rgba(255, 255, 255, 0.85);
  backdrop-filter: blur(14px) saturate(120%);
  -webkit-backdrop-filter: blur(14px) saturate(120%);
  border: 1px solid rgba(255, 255, 255, 0.7);
  border-radius: 18px;
  padding: 16px 20px;
  transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.06);
  position: relative;
}

.composer:focus-within {
  border-color: rgba(13, 71, 161, 0.6);
  box-shadow: 0 0 0 4px rgba(13, 71, 161, 0.15), 0 8px 16px rgba(0, 0, 0, 0.08);
}

.attachments {
  margin-bottom: 10px;
  padding-bottom: 10px;
  border-bottom: 1px solid #f3f4f6;
}

.attachments .tip {
  font-size: 12px;
  color: #6b7280;
  margin-bottom: 8px;
}

.attachment-list {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

.attachment-card {
  display: flex;
  align-items: center;
  gap: 8px;
  background: #f9fafb;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  padding: 6px 10px;
  position: relative;
  max-width: 200px;
}

.file-icon {
  width: 24px;
  height: 24px;
  border-radius: 6px;
  background: #dbeafe;
  color: #3b82f6;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  flex-shrink: 0;
}

.file-meta {
  flex: 1;
  overflow: hidden;
}

.file-name {
  font-size: 12px;
  color: #374151;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.file-size {
  font-size: 11px;
  color: #9ca3af;
}

.att-remove {
  position: absolute;
  top: -6px;
  right: -6px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 1px solid #ffffff;
  background: #ef4444;
  color: #ffffff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  font-size: 12px;
}

.att-remove:hover {
  background: #dc2626;
}

.input-row {
  display: flex;
  align-items: flex-end;
  gap: 8px;
}

.input {
  flex: 1;
  min-height: 24px;
  max-height: 120px;
  border: none;
  outline: none;
  font-size: 14px;
  color: #111827;
  background: transparent;
  resize: none;
  font-family: inherit;
  line-height: 1.5;
}

.input::placeholder {
  color: #9ca3af;
}

.actions {
  display: flex;
  align-items: center;
  gap: 6px;
}

.hidden-file {
  display: none;
}

.popover-center-ref {
  position: absolute;
  left: 50%;
  top: 0;
  width: 0;
  height: 0;
}

.attach,
.send,
.stop {
  width: 36px;
  height: 36px;
  border-radius: 12px;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
  flex-shrink: 0;
}

.attach {
  background: transparent;
  color: #9ca3af;
}

.attach:hover:not(:disabled) {
  background: rgba(59, 130, 246, 0.08);
  color: var(--c-law-blue);
  transform: scale(1.05);
}

.send {
  background: linear-gradient(135deg, var(--c-law-blue) 0%, var(--c-law-dark) 100%);
  color: #ffffff;
  box-shadow: 0 4px 16px rgba(59, 130, 246, 0.25);
}

.send:hover:not(:disabled) {
  transform: scale(1.05);
  box-shadow: 0 6px 24px rgba(59, 130, 246, 0.35);
}

.send:active:not(:disabled) {
  transform: scale(0.95);
}

.stop {
  background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
  color: #ffffff;
  box-shadow: 0 4px 16px rgba(239, 68, 68, 0.25);
}

.stop:hover {
  transform: scale(1.05);
  box-shadow: 0 6px 24px rgba(239, 68, 68, 0.35);
}

.stop:active {
  transform: scale(0.95);
}

.attach:disabled,
.send:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  transform: none !important;
}

.attach i,
.send i,
.stop i {
  font-size: 16px;
}

/* 下一步问题建议（消息底部）*/
.suggested-questions-container {
  margin: 16px auto;
  padding: 14px 18px;
  background: linear-gradient(135deg, #f9fafb 0%, #ffffff 100%);
  border: 1px solid #e5e7eb;
  border-radius: 12px;
  animation: fadeIn 0.3s ease;
  box-shadow: 0 6px 20px rgba(17, 24, 39, 0.06);
  max-width: 900px;
  width: calc(100% - 48px);
  box-sizing: border-box;
}

/* 响应式：超大屏幕 */
@media (min-width: 1400px) {
  .suggested-questions-container {
    max-width: 1000px;
  }
}

/* 响应式：中等屏幕 */
@media (max-width: 768px) {
  .suggested-questions-container {
    width: calc(100% - 32px);
    margin: 16px auto;
  }
}

.suggested-questions-title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
  color: #6b7280;
  font-size: 13px;
  font-weight: 600;
}

.suggested-questions-title i {
  font-size: 16px;
  color: var(--c-law-blue);
}

.suggested-questions-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.suggested-question-chip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  background: #ffffff;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.2s;
}

.suggested-question-chip:hover {
  background: #f1f5f9;
  border-color: var(--c-law-blue);
  transform: translateX(4px);
}

.suggested-question-chip span {
  flex: 1;
  font-size: 14px;
  color: #374151;
  line-height: 1.5;
}

.suggested-question-chip i {
  font-size: 14px;
  color: #9ca3af;
  transition: color 0.2s;
}

.suggested-question-chip:hover i {
  color: var(--c-law-blue);
}

/* 文件链接 */
.message-text ::v-deep .file-link-wrapper {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  background: #f3f6ff;
  border: 1px solid #e0e7ff;
  border-radius: 6px;
  margin: 4px 0;
  transition: all 0.2s;
}

.message-text ::v-deep .file-link-wrapper:hover {
  background: #eef2ff;
  border-color: var(--c-primary);
  transform: translateX(2px);
}

.message-text ::v-deep .file-link-wrapper i {
  font-size: 18px;
  color: var(--c-law-blue);
}

.message-text ::v-deep .file-link {
  color: #374151;
  text-decoration: none;
  font-size: 14px;
  font-weight: 500;
  transition: color 0.2s;
}

.message-text ::v-deep .file-link:hover {
  color: var(--c-law-blue);
  text-decoration: underline;
}

@supports ((-webkit-backdrop-filter: blur(8px)) or (backdrop-filter: blur(8px))) {
  .sidebar {
    background: rgba(255, 255, 255, 0.6);
    -webkit-backdrop-filter: blur(12px);
    backdrop-filter: blur(12px);
  }

  .composer {
    background: rgba(255, 255, 255, 0.72);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
  }

  .message-wrapper.assistant .message-text {
    background: rgba(255, 255, 255, 0.9);
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
  }
}

/* 用户消息内容容器 */
.user-message-content {
  width: 100%;
}

.user-message-text {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.user-message-text-body {
  width: 100%;
  color: inherit;
}

/* 用户消息框体内的文件显示 */
.user-files-inline {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.user-file-inline-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  width: 100%;
  background: rgba(255, 255, 255, 0.95);
  border: 1px solid rgba(255, 255, 255, 0.9);
  border-radius: 6px;
  transition: all 0.2s;
  cursor: pointer;
}

.user-file-inline-item:hover {
  background: #eff6ff;
  border-color: #bfdbfe;
  transform: translateX(-2px);
}

.user-file-inline-item i {
  font-size: 16px;
  color: #3b82f6;
  flex-shrink: 0;
}

.user-file-inline-name {
  flex: 1;
  font-size: 13px;
  color: #1f2937;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 引用和归属 */
.message-text ::v-deep .references-section {
  margin-top: 16px;
  padding: 16px;
  background: #f8fafc;
  border-left: 4px solid var(--c-law-blue);
  border-radius: 8px;
}

.message-text ::v-deep .references-title {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 12px;
  font-size: 14px;
  font-weight: 600;
  color: #374151;
}

.message-text ::v-deep .references-title i {
  font-size: 16px;
  color: var(--c-law-blue);
}

.message-text ::v-deep .reference-item {
  margin-top: 12px;
  padding: 12px;
  background: #ffffff;
  border: 1px solid #e5e7eb;
  border-radius: 6px;
}

.message-text ::v-deep .reference-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

.message-text ::v-deep .reference-index {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  background: var(--c-law-blue);
  color: #ffffff;
  font-size: 12px;
  font-weight: 600;
  border-radius: 50%;
}

.message-text ::v-deep .reference-dataset {
  font-size: 13px;
  font-weight: 600;
  color: #374151;
}

.message-text ::v-deep .reference-score {
  margin-left: auto;
  font-size: 12px;
  color: #6b7280;
  background: #f3f4f6;
  padding: 2px 8px;
  border-radius: 4px;
}

.message-text ::v-deep .reference-content {
  font-size: 13px;
  color: #6b7280;
  line-height: 1.6;
  font-style: italic;
}

/* 加载动画 */
.typing-indicator {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  padding: 20px 25px;
  background: rgba(255, 255, 255, 0.85);
  backdrop-filter: blur(14px) saturate(120%);
  -webkit-backdrop-filter: blur(14px) saturate(120%);
  border: 2px solid rgba(13, 71, 161, 0.12);
  border-radius: 24px;
  border-top-left-radius: 8px;
  width: fit-content;
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.06), inset 0 0 0 1px rgba(255, 255, 255, 0.5);
}

.typing-text {
  font-size: 15px;
  color: #3b82f6;
  font-weight: 500;
  letter-spacing: 0.5px;
}

.typing-dots {
  display: flex;
  gap: 6px;
  align-items: center;
}

.typing-dots span {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--c-law-blue) 0%, var(--c-law-dark) 100%);
  animation: typing 1.4s infinite ease-in-out;
}

.typing-dots span:nth-child(1) {
  animation-delay: 0s;
}

.typing-dots span:nth-child(2) {
  animation-delay: 0.2s;
}

.typing-dots span:nth-child(3) {
  animation-delay: 0.4s;
}

@keyframes typing {
  0%, 60%, 100% {
    opacity: 0.3;
    transform: scale(0.7);
  }
  30% {
    opacity: 1;
    transform: scale(1.1);
  }
}
</style>
